Merge "Add line break between PLMN and SPN in windowshade." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 8651c4d..179c515 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -947,6 +947,17 @@
  visibility="public"
 >
 </field>
+<field name="SET_ALARM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;com.android.alarm.permission.SET_ALARM&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SET_ALWAYS_FINISH"
  type="java.lang.String"
  transient="false"
@@ -4695,17 +4706,6 @@
  visibility="public"
 >
 </field>
-<field name="immersive"
- type="int"
- transient="false"
- volatile="false"
- value="16843456"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="inAnimation"
  type="int"
  transient="false"
@@ -5817,17 +5817,6 @@
  visibility="public"
 >
 </field>
-<field name="kraken_resource_pad57"
- type="int"
- transient="false"
- volatile="false"
- value="16843464"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="kraken_resource_pad6"
  type="int"
  transient="false"
@@ -6422,17 +6411,6 @@
  visibility="public"
 >
 </field>
-<field name="logo"
- type="int"
- transient="false"
- volatile="false"
- value="16843454"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="longClickable"
  type="int"
  transient="false"
@@ -9513,6 +9491,17 @@
  visibility="public"
 >
 </field>
+<field name="textSelectHandleWindowStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843464"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textSize"
  type="int"
  transient="false"
@@ -24499,6 +24488,623 @@
 </parameter>
 </method>
 </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"
@@ -48910,6 +49516,17 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_AUDIO_LOW_LATENCY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.audio.low_latency&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_BLUETOOTH"
  type="java.lang.String"
  transient="false"
@@ -49042,6 +49659,17 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_SENSOR_BAROMETER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.sensor.barometer&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_SENSOR_COMPASS"
  type="java.lang.String"
  transient="false"
@@ -49053,6 +49681,17 @@
  visibility="public"
 >
 </field>
+<field name="FEATURE_SENSOR_GYROSCOPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.sensor.gyroscope&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_SENSOR_LIGHT"
  type="java.lang.String"
  transient="false"
@@ -62251,6 +62890,146 @@
 >
 </field>
 </class>
+<class name="BitmapRegionDecoder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="decodeRegion"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rect" type="android.graphics.Rect">
+</parameter>
+<parameter name="options" type="android.graphics.BitmapFactory.Options">
+</parameter>
+</method>
+<method name="getHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isRecycled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="is" type="java.io.InputStream">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pathName" type="java.lang.String">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="recycle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <class name="BitmapShader"
  extends="android.graphics.Shader"
  abstract="false"
@@ -76944,11 +77723,11 @@
  visibility="public"
 >
 </field>
-<field name="FOCUS_MODE_CONTINUOUS"
+<field name="FOCUS_MODE_CONTINUOUS_VIDEO"
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;continuous&quot;"
+ value="&quot;continuous-video&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -82537,8 +83316,8 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="isImplemented"
- return="java.lang.Boolean"
+<method name="isPresent"
+ return="boolean"
  abstract="false"
  native="false"
  synchronized="false"
@@ -82549,75 +83328,6 @@
 >
 </method>
 </class>
-<class name="GeocoderParams"
- 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="getClientPackage"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLocale"
- return="java.util.Locale"
- 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="parcel" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="GpsSatellite"
  extends="java.lang.Object"
  abstract="false"
@@ -84209,396 +84919,6 @@
 </field>
 </class>
 </package>
-<package name="android.location.provider"
->
-<class name="GeocodeProvider"
- extends="java.lang.Object"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="GeocodeProvider"
- type="android.location.provider.GeocodeProvider"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="getBinder"
- return="android.os.IBinder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onGetFromLocation"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="latitude" type="double">
-</parameter>
-<parameter name="longitude" type="double">
-</parameter>
-<parameter name="maxResults" type="int">
-</parameter>
-<parameter name="params" type="android.location.GeocoderParams">
-</parameter>
-<parameter name="addrs" type="java.util.List&lt;android.location.Address&gt;">
-</parameter>
-</method>
-<method name="onGetFromLocationName"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="locationName" type="java.lang.String">
-</parameter>
-<parameter name="lowerLeftLatitude" type="double">
-</parameter>
-<parameter name="lowerLeftLongitude" type="double">
-</parameter>
-<parameter name="upperRightLatitude" type="double">
-</parameter>
-<parameter name="upperRightLongitude" type="double">
-</parameter>
-<parameter name="maxResults" type="int">
-</parameter>
-<parameter name="params" type="android.location.GeocoderParams">
-</parameter>
-<parameter name="addrs" type="java.util.List&lt;android.location.Address&gt;">
-</parameter>
-</method>
-</class>
-<class name="LocationProvider"
- extends="java.lang.Object"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="LocationProvider"
- type="android.location.provider.LocationProvider"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="getBinder"
- return="android.os.IBinder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onAddListener"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uid" type="int">
-</parameter>
-<parameter name="ws" type="android.os.WorkSource">
-</parameter>
-</method>
-<method name="onDisable"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onEnable"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onEnableLocationTracking"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enable" type="boolean">
-</parameter>
-</method>
-<method name="onGetAccuracy"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onGetInternalState"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onGetPowerRequirement"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onGetStatus"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="extras" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="onGetStatusUpdateTime"
- return="long"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onHasMonetaryCost"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onMeetsCriteria"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="criteria" type="android.location.Criteria">
-</parameter>
-</method>
-<method name="onRemoveListener"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uid" type="int">
-</parameter>
-<parameter name="ws" type="android.os.WorkSource">
-</parameter>
-</method>
-<method name="onRequiresCell"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onRequiresNetwork"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onRequiresSatellite"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onSendExtraCommand"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="command" type="java.lang.String">
-</parameter>
-<parameter name="extras" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="onSetMinTime"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="minTime" type="long">
-</parameter>
-<parameter name="ws" type="android.os.WorkSource">
-</parameter>
-</method>
-<method name="onSupportsAltitude"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onSupportsBearing"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onSupportsSpeed"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onUpdateLocation"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="location" type="android.location.Location">
-</parameter>
-</method>
-<method name="onUpdateNetworkState"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="state" type="int">
-</parameter>
-<parameter name="info" type="android.net.NetworkInfo">
-</parameter>
-</method>
-<method name="reportLocation"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="location" type="android.location.Location">
-</parameter>
-</method>
-</class>
-</package>
 <package name="android.media"
 >
 <class name="AsyncPlayer"
@@ -84650,969 +84970,6 @@
 >
 </method>
 </class>
-<class name="AudioEffect"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="AudioEffect"
- type="android.media.AudioEffect"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.util.UUID">
-</parameter>
-<parameter name="uuid" type="java.util.UUID">
-</parameter>
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="byteArrayToInt"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-</method>
-<method name="byteArrayToInt"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-<parameter name="offset" type="int">
-</parameter>
-</method>
-<method name="byteArrayToShort"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-</method>
-<method name="byteArrayToShort"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="valueBuf" type="byte[]">
-</parameter>
-<parameter name="offset" type="int">
-</parameter>
-</method>
-<method name="checkState"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="methodName" type="java.lang.String">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="checkStatus"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="status" type="int">
-</parameter>
-</method>
-<method name="command"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cmdCode" type="int">
-</parameter>
-<parameter name="command" type="byte[]">
-</parameter>
-<parameter name="reply" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="concatArrays"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arrays" type="byte...">
-</parameter>
-</method>
-<method name="getDescriptor"
- return="android.media.AudioEffect.Descriptor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getEnabled"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getId"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="byte[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="int[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="int[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="short[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="hasControl"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="intToByteArray"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="int">
-</parameter>
-</method>
-<method name="queryEffects"
- return="android.media.AudioEffect.Descriptor[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="release"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setControlStatusListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.AudioEffect.OnControlStatusChangeListener">
-</parameter>
-</method>
-<method name="setEnableStatusListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.AudioEffect.OnEnableStatusChangeListener">
-</parameter>
-</method>
-<method name="setEnabled"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enabled" type="boolean">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="byte[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="int[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="short[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="param" type="int[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.AudioEffect.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="shortToByteArray"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="short">
-</parameter>
-</method>
-<field name="ALREADY_EXISTS"
- type="int"
- transient="false"
- volatile="false"
- value="-2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_AUXILIARY"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;Auxiliary&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_INSERT"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;Insert&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_BASS_BOOST"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_ENV_REVERB"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_EQUALIZER"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_NULL"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_PRESET_REVERB"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_TYPE_VIRTUALIZER"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="-1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_BAD_VALUE"
- type="int"
- transient="false"
- volatile="false"
- value="-4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_DEAD_OBJECT"
- type="int"
- transient="false"
- volatile="false"
- value="-7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INVALID_OPERATION"
- type="int"
- transient="false"
- volatile="false"
- value="-5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_INIT"
- type="int"
- transient="false"
- volatile="false"
- value="-3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_MEMORY"
- type="int"
- transient="false"
- volatile="false"
- value="-6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NATIVE_EVENT_CONTROL_STATUS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NATIVE_EVENT_ENABLED_STATUS"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NATIVE_EVENT_PARAMETER_CHANGED"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATE_INITIALIZED"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATE_UNINITIALIZED"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SUCCESS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mListenerLock"
- type="java.lang.Object"
- transient="false"
- volatile="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mNativeEventHandler"
- type="android.media.AudioEffect.NativeEventHandler"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="AudioEffect.Descriptor"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="AudioEffect.Descriptor"
- type="android.media.AudioEffect.Descriptor"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="AudioEffect.Descriptor"
- type="android.media.AudioEffect.Descriptor"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.lang.String">
-</parameter>
-<parameter name="uuid" type="java.lang.String">
-</parameter>
-<parameter name="connectMode" type="java.lang.String">
-</parameter>
-<parameter name="name" type="java.lang.String">
-</parameter>
-<parameter name="implementor" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="mConnectMode"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mImplementor"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mName"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mType"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="mUuid"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="AudioEffect.NativeEventHandler"
- extends="android.os.Handler"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="private"
->
-<constructor name="AudioEffect.NativeEventHandler"
- type="android.media.AudioEffect.NativeEventHandler"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="ae" type="android.media.AudioEffect">
-</parameter>
-<parameter name="looper" type="android.os.Looper">
-</parameter>
-</constructor>
-</class>
-<interface name="AudioEffect.OnControlStatusChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onControlStatusChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.AudioEffect">
-</parameter>
-<parameter name="controlGranted" type="boolean">
-</parameter>
-</method>
-</interface>
-<interface name="AudioEffect.OnEnableStatusChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onEnableStatusChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.AudioEffect">
-</parameter>
-<parameter name="enabled" type="boolean">
-</parameter>
-</method>
-</interface>
-<interface name="AudioEffect.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.AudioEffect">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="byte[]">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-</method>
-</interface>
 <class name="AudioFormat"
  extends="java.lang.Object"
  abstract="false"
@@ -88454,217 +87811,6 @@
 </parameter>
 </method>
 </interface>
-<class name="BassBoost"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BassBoost"
- type="android.media.BassBoost"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getProperties"
- return="android.media.BassBoost.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoundedStrength"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getStrengthSupported"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.BassBoost.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.BassBoost.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setStrength"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="strength" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_STRENGTH"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_STRENGTH_SUPPORTED"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="BassBoost.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.BassBoost">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-</interface>
-<class name="BassBoost.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BassBoost.Settings"
- type="android.media.BassBoost.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="BassBoost.Settings"
- type="android.media.BassBoost.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="strength"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="CamcorderProfile"
  extends="java.lang.Object"
  abstract="false"
@@ -88922,1178 +88068,6 @@
 >
 </field>
 </class>
-<class name="EnvironmentalReverb"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="EnvironmentalReverb"
- type="android.media.EnvironmentalReverb"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getDecayHFRatio"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDecayTime"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDensity"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDiffusion"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getProperties"
- return="android.media.EnvironmentalReverb.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReflectionsDelay"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReflectionsLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReverbDelay"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReverbLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoomHFLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoomLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDecayHFRatio"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="decayHFRatio" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDecayTime"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="decayTime" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDensity"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="density" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDiffusion"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="diffusion" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.EnvironmentalReverb.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.EnvironmentalReverb.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReflectionsDelay"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reflectionsDelay" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReflectionsLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reflectionsLevel" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReverbDelay"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reverbDelay" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReverbLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reverbLevel" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setRoomHFLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="roomHF" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setRoomLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="room" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_DECAY_HF_RATIO"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DECAY_TIME"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DENSITY"
- type="int"
- transient="false"
- volatile="false"
- value="9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DIFFUSION"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REFLECTIONS_DELAY"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REFLECTIONS_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REVERB_DELAY"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REVERB_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_ROOM_HF_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_ROOM_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="EnvironmentalReverb.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.EnvironmentalReverb">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-</interface>
-<class name="EnvironmentalReverb.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="EnvironmentalReverb.Settings"
- type="android.media.EnvironmentalReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="EnvironmentalReverb.Settings"
- type="android.media.EnvironmentalReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="decayHFRatio"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="decayTime"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="density"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="diffusion"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reflectionsDelay"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reflectionsLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reverbDelay"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reverbLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="roomHFLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="roomLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="Equalizer"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Equalizer"
- type="android.media.Equalizer"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getBand"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="frequency" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandFreqRange"
- return="int[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandLevelRange"
- return="short[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getCenterFreq"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getCurrentPreset"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getNumberOfBands"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getNumberOfPresets"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getPresetName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-</method>
-<method name="getProperties"
- return="android.media.Equalizer.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setBandLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<parameter name="level" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.Equalizer.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.Equalizer.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="usePreset"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_BAND_FREQ_RANGE"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_BAND_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_CENTER_FREQ"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_CURRENT_PRESET"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_BAND"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_NUM_OF_PRESETS"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_PRESET_NAME"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_LEVEL_RANGE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_NUM_BANDS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_STRING_SIZE_MAX"
- type="int"
- transient="false"
- volatile="false"
- value="32"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="Equalizer.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.Equalizer">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param1" type="int">
-</parameter>
-<parameter name="param2" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-</interface>
-<class name="Equalizer.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Equalizer.Settings"
- type="android.media.Equalizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="Equalizer.Settings"
- type="android.media.Equalizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="bandLevels"
- type="short[]"
- transient="false"
- volatile="false"
- value="null"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="curPreset"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="numBands"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="ExifInterface"
  extends="java.lang.Object"
  abstract="false"
@@ -92758,270 +90732,6 @@
 </parameter>
 </method>
 </interface>
-<class name="PresetReverb"
- extends="android.media.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PresetReverb"
- type="android.media.PresetReverb"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getPreset"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getProperties"
- return="android.media.PresetReverb.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.PresetReverb.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setPreset"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.PresetReverb.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_PRESET"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_LARGEHALL"
- type="short"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_LARGEROOM"
- type="short"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_MEDIUMHALL"
- type="short"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_MEDIUMROOM"
- type="short"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_NONE"
- type="short"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_PLATE"
- type="short"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_SMALLROOM"
- type="short"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="PresetReverb.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.PresetReverb">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-</interface>
-<class name="PresetReverb.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PresetReverb.Settings"
- type="android.media.PresetReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="PresetReverb.Settings"
- type="android.media.PresetReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="preset"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="Ringtone"
  extends="java.lang.Object"
  abstract="false"
@@ -95096,16 +92806,494 @@
 >
 </field>
 </class>
-<class name="Virtualizer"
- extends="android.media.AudioEffect"
+</package>
+<package name="android.media.audiofx"
+>
+<class name="AudioEffect"
+ extends="java.lang.Object"
  abstract="false"
  static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-<constructor name="Virtualizer"
- type="android.media.Virtualizer"
+<method name="getDescriptor"
+ return="android.media.audiofx.AudioEffect.Descriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="getEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="getId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="hasControl"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<method name="queryEffects"
+ return="android.media.audiofx.AudioEffect.Descriptor[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="release"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setControlStatusListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.AudioEffect.OnControlStatusChangeListener">
+</parameter>
+</method>
+<method name="setEnableStatusListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.AudioEffect.OnEnableStatusChangeListener">
+</parameter>
+</method>
+<method name="setEnabled"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+</method>
+<field name="ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ALREADY_EXISTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_GAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_MOVIE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_MUSIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE_VOICE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_AUXILIARY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;Auxiliary&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_INSERT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;Insert&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_BAD_VALUE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_DEAD_OBJECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_INVALID_OPERATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_NO_INIT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_NO_MEMORY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_AUDIO_SESSION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.extra.AUDIO_SESSION&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_CONTENT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.extra.CONTENT_TYPE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PACKAGE_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.extra.PACKAGE_NAME&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SUCCESS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AudioEffect.Descriptor"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AudioEffect.Descriptor"
+ type="android.media.audiofx.AudioEffect.Descriptor"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="AudioEffect.Descriptor"
+ type="android.media.audiofx.AudioEffect.Descriptor"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="uuid" type="java.lang.String">
+</parameter>
+<parameter name="connectMode" type="java.lang.String">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="implementor" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="connectMode"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="implementor"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="name"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="type"
+ type="java.util.UUID"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="uuid"
+ type="java.util.UUID"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="AudioEffect.OnControlStatusChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onControlStatusChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.AudioEffect">
+</parameter>
+<parameter name="controlGranted" type="boolean">
+</parameter>
+</method>
+</interface>
+<interface name="AudioEffect.OnEnableStatusChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onEnableStatusChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.AudioEffect">
+</parameter>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
+</interface>
+<class name="BassBoost"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BassBoost"
+ type="android.media.audiofx.BassBoost"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -95125,7 +93313,7 @@
 </exception>
 </constructor>
 <method name="getProperties"
- return="android.media.Virtualizer.Settings"
+ return="android.media.audiofx.BassBoost.Settings"
  abstract="false"
  native="false"
  synchronized="false"
@@ -95179,7 +93367,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.media.Virtualizer.OnParameterChangeListener">
+<parameter name="listener" type="android.media.audiofx.BassBoost.OnParameterChangeListener">
 </parameter>
 </method>
 <method name="setProperties"
@@ -95192,7 +93380,1654 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="settings" type="android.media.Virtualizer.Settings">
+<parameter name="settings" type="android.media.audiofx.BassBoost.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setStrength"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="strength" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_STRENGTH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_STRENGTH_SUPPORTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="BassBoost.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.BassBoost">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param" type="int">
+</parameter>
+<parameter name="value" type="short">
+</parameter>
+</method>
+</interface>
+<class name="BassBoost.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BassBoost.Settings"
+ type="android.media.audiofx.BassBoost.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="BassBoost.Settings"
+ type="android.media.audiofx.BassBoost.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="strength"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="EnvironmentalReverb"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="EnvironmentalReverb"
+ type="android.media.audiofx.EnvironmentalReverb"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getDecayHFRatio"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getDecayTime"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getDensity"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getDiffusion"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getProperties"
+ return="android.media.audiofx.EnvironmentalReverb.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReflectionsDelay"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReflectionsLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReverbDelay"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getReverbLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getRoomHFLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getRoomLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDecayHFRatio"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="decayHFRatio" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDecayTime"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="decayTime" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDensity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="density" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setDiffusion"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="diffusion" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.EnvironmentalReverb.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.EnvironmentalReverb.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReflectionsDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reflectionsDelay" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReflectionsLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reflectionsLevel" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReverbDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reverbDelay" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setReverbLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reverbLevel" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setRoomHFLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="roomHF" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setRoomLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="room" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_DECAY_HF_RATIO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_DECAY_TIME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_DENSITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_DIFFUSION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REFLECTIONS_DELAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REFLECTIONS_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REVERB_DELAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_REVERB_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_ROOM_HF_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_ROOM_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="EnvironmentalReverb.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.EnvironmentalReverb">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param" type="int">
+</parameter>
+<parameter name="value" type="int">
+</parameter>
+</method>
+</interface>
+<class name="EnvironmentalReverb.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="EnvironmentalReverb.Settings"
+ type="android.media.audiofx.EnvironmentalReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="EnvironmentalReverb.Settings"
+ type="android.media.audiofx.EnvironmentalReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="decayHFRatio"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="decayTime"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="density"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="diffusion"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reflectionsDelay"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reflectionsLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reverbDelay"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reverbLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="roomHFLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="roomLevel"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Equalizer"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Equalizer"
+ type="android.media.audiofx.Equalizer"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getBand"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="frequency" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getBandFreqRange"
+ return="int[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getBandLevel"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getBandLevelRange"
+ return="short[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getCenterFreq"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getCurrentPreset"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getNumberOfBands"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getNumberOfPresets"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getPresetName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preset" type="short">
+</parameter>
+</method>
+<method name="getProperties"
+ return="android.media.audiofx.Equalizer.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setBandLevel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="band" type="short">
+</parameter>
+<parameter name="level" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.Equalizer.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.Equalizer.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="usePreset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preset" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_BAND_FREQ_RANGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_BAND_LEVEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_CENTER_FREQ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_CURRENT_PRESET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_GET_BAND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_GET_NUM_OF_PRESETS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_GET_PRESET_NAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_LEVEL_RANGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_NUM_BANDS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARAM_STRING_SIZE_MAX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="Equalizer.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.Equalizer">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param1" type="int">
+</parameter>
+<parameter name="param2" type="int">
+</parameter>
+<parameter name="value" type="int">
+</parameter>
+</method>
+</interface>
+<class name="Equalizer.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Equalizer.Settings"
+ type="android.media.audiofx.Equalizer.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="Equalizer.Settings"
+ type="android.media.audiofx.Equalizer.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="bandLevels"
+ type="short[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="curPreset"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="numBands"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="PresetReverb"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PresetReverb"
+ type="android.media.audiofx.PresetReverb"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getPreset"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getProperties"
+ return="android.media.audiofx.PresetReverb.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.PresetReverb.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setPreset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preset" type="short">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.PresetReverb.Settings">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<field name="PARAM_PRESET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_LARGEHALL"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_LARGEROOM"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_MEDIUMHALL"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_MEDIUMROOM"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_NONE"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_PLATE"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PRESET_SMALLROOM"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="PresetReverb.OnParameterChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onParameterChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="effect" type="android.media.audiofx.PresetReverb">
+</parameter>
+<parameter name="status" type="int">
+</parameter>
+<parameter name="param" type="int">
+</parameter>
+<parameter name="value" type="short">
+</parameter>
+</method>
+</interface>
+<class name="PresetReverb.Settings"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PresetReverb.Settings"
+ type="android.media.audiofx.PresetReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="PresetReverb.Settings"
+ type="android.media.audiofx.PresetReverb.Settings"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="preset"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Virtualizer"
+ extends="android.media.audiofx.AudioEffect"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Virtualizer"
+ type="android.media.audiofx.Virtualizer"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+<parameter name="audioSession" type="int">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="RuntimeException" type="java.lang.RuntimeException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</constructor>
+<method name="getProperties"
+ return="android.media.audiofx.Virtualizer.Settings"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getRoundedStrength"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="IllegalStateException" type="java.lang.IllegalStateException">
+</exception>
+<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
+</exception>
+</method>
+<method name="getStrengthSupported"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setParameterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.media.audiofx.Virtualizer.OnParameterChangeListener">
+</parameter>
+</method>
+<method name="setProperties"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="settings" type="android.media.audiofx.Virtualizer.Settings">
 </parameter>
 <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
 </exception>
@@ -95260,7 +95095,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="effect" type="android.media.Virtualizer">
+<parameter name="effect" type="android.media.audiofx.Virtualizer">
 </parameter>
 <parameter name="status" type="int">
 </parameter>
@@ -95279,7 +95114,7 @@
  visibility="public"
 >
 <constructor name="Virtualizer.Settings"
- type="android.media.Virtualizer.Settings"
+ type="android.media.audiofx.Virtualizer.Settings"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -95287,7 +95122,7 @@
 >
 </constructor>
 <constructor name="Virtualizer.Settings"
- type="android.media.Virtualizer.Settings"
+ type="android.media.audiofx.Virtualizer.Settings"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -95316,7 +95151,7 @@
  visibility="public"
 >
 <constructor name="Visualizer"
- type="android.media.Visualizer"
+ type="android.media.audiofx.Visualizer"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -95454,7 +95289,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="listener" type="android.media.Visualizer.OnDataCaptureListener">
+<parameter name="listener" type="android.media.audiofx.Visualizer.OnDataCaptureListener">
 </parameter>
 <parameter name="rate" type="int">
 </parameter>
@@ -95617,7 +95452,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="visualizer" type="android.media.Visualizer">
+<parameter name="visualizer" type="android.media.audiofx.Visualizer">
 </parameter>
 <parameter name="fft" type="byte[]">
 </parameter>
@@ -95634,7 +95469,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="visualizer" type="android.media.Visualizer">
+<parameter name="visualizer" type="android.media.audiofx.Visualizer">
 </parameter>
 <parameter name="waveform" type="byte[]">
 </parameter>
@@ -96129,567 +95964,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="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_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="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.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="setMediaType"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="mediaType" type="java.lang.String">
-</parameter>
-</method>
-<method name="setRequestHeader"
- 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="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.String">
-</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>
-<field name="NETWORK_WIMAX"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="LocalServerSocket"
  extends="java.lang.Object"
  abstract="false"
@@ -132455,6 +131729,67 @@
 </package>
 <package name="android.provider"
 >
+<class name="AlarmClock"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AlarmClock"
+ type="android.provider.AlarmClock"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="ACTION_SET_ALARM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.SET_ALARM&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_HOUR"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.HOUR&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_MESSAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.MESSAGE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_MINUTES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.MINUTES&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <interface name="BaseColumns"
  abstract="true"
  static="false"
@@ -138285,17 +137620,6 @@
  deprecated="not deprecated"
  visibility="protected"
 >
-<field name="CONTACT_CHAT_CAPABILITY"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;contact_chat_capability&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="CONTACT_PRESENCE"
  type="java.lang.String"
  transient="false"
@@ -140273,50 +139597,6 @@
  visibility="public"
 >
 </field>
-<field name="CAPABILITY_HAS_CAMERA"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CAPABILITY_HAS_VIDEO_PLAYBACK_ONLY"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CAPABILITY_HAS_VOICE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHAT_CAPABILITY"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;chat_capability&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="DO_NOT_DISTURB"
  type="int"
  transient="false"
@@ -143864,6 +143144,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_MANAGE_APPLICATIONS_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -166751,19 +166042,6 @@
 <parameter name="event" type="android.view.MotionEvent">
 </parameter>
 </method>
-<method name="setCursorController"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cursorController" type="android.widget.TextView.CursorController">
-</parameter>
-</method>
 </class>
 <class name="BaseKeyListener"
  extends="android.text.method.MetaKeyKeyListener"
@@ -223884,121 +223162,6 @@
 >
 </method>
 </class>
-<interface name="TextView.CursorController"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="draw"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="canvas" type="android.graphics.Canvas">
-</parameter>
-</method>
-<method name="getOffsetX"
- return="float"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getOffsetY"
- return="float"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="hide"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isShowing"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onTouchEvent"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="android.view.MotionEvent">
-</parameter>
-</method>
-<method name="show"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="updatePosition"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="x" type="int">
-</parameter>
-<parameter name="y" type="int">
-</parameter>
-</method>
-<field name="FADE_OUT_DURATION"
- type="int"
- transient="false"
- volatile="false"
- value="400"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</interface>
 <interface name="TextView.OnEditorActionListener"
  abstract="true"
  static="true"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 301883f..f2aa91f 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -19,6 +19,7 @@
 package com.android.commands.am;
 
 import android.app.ActivityManagerNative;
+import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IInstrumentationWatcher;
 import android.app.Instrumentation;
@@ -33,8 +34,11 @@
 import android.util.AndroidException;
 import android.view.IWindowManager;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.net.URISyntaxException;
 import java.util.Iterator;
@@ -98,6 +102,8 @@
             sendBroadcast();
         } else if (op.equals("profile")) {
             runProfile();
+        } else if (op.equals("monitor")) {
+            runMonitor();
         } else {
             throw new IllegalArgumentException("Unknown command: " + op);
         }
@@ -424,6 +430,303 @@
         }
     }
 
+    class MyActivityController extends IActivityController.Stub {
+        final String mGdbPort;
+
+        static final int STATE_NORMAL = 0;
+        static final int STATE_CRASHED = 1;
+        static final int STATE_EARLY_ANR = 2;
+        static final int STATE_ANR = 3;
+
+        int mState;
+
+        static final int RESULT_DEFAULT = 0;
+
+        static final int RESULT_CRASH_DIALOG = 0;
+        static final int RESULT_CRASH_KILL = 1;
+
+        static final int RESULT_EARLY_ANR_CONTINUE = 0;
+        static final int RESULT_EARLY_ANR_KILL = 1;
+
+        static final int RESULT_ANR_DIALOG = 0;
+        static final int RESULT_ANR_KILL = 1;
+        static final int RESULT_ANR_WAIT = 1;
+
+        int mResult;
+
+        Process mGdbProcess;
+        Thread mGdbThread;
+        boolean mGotGdbPrint;
+
+        MyActivityController(String gdbPort) {
+            mGdbPort = gdbPort;
+        }
+
+        @Override
+        public boolean activityResuming(String pkg) throws RemoteException {
+            synchronized (this) {
+                System.out.println("** Activity resuming: " + pkg);
+            }
+            return true;
+        }
+
+        @Override
+        public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
+            synchronized (this) {
+                System.out.println("** Activity starting: " + pkg);
+            }
+            return true;
+        }
+
+        @Override
+        public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
+                long timeMillis, String stackTrace) throws RemoteException {
+            synchronized (this) {
+                System.out.println("** ERROR: PROCESS CRASHED");
+                System.out.println("processName: " + processName);
+                System.out.println("processPid: " + pid);
+                System.out.println("shortMsg: " + shortMsg);
+                System.out.println("longMsg: " + longMsg);
+                System.out.println("timeMillis: " + timeMillis);
+                System.out.println("stack:");
+                System.out.print(stackTrace);
+                System.out.println("#");
+                int result = waitControllerLocked(pid, STATE_CRASHED);
+                return result == RESULT_CRASH_KILL ? false : true;
+            }
+        }
+
+        @Override
+        public int appEarlyNotResponding(String processName, int pid, String annotation)
+                throws RemoteException {
+            synchronized (this) {
+                System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING");
+                System.out.println("processName: " + processName);
+                System.out.println("processPid: " + pid);
+                System.out.println("annotation: " + annotation);
+                int result = waitControllerLocked(pid, STATE_EARLY_ANR);
+                if (result == RESULT_EARLY_ANR_KILL) return -1;
+                return 0;
+            }
+        }
+
+        @Override
+        public int appNotResponding(String processName, int pid, String processStats)
+                throws RemoteException {
+            synchronized (this) {
+                System.out.println("** ERROR: PROCESS NOT RESPONDING");
+                System.out.println("processName: " + processName);
+                System.out.println("processPid: " + pid);
+                System.out.println("processStats:");
+                System.out.print(processStats);
+                System.out.println("#");
+                int result = waitControllerLocked(pid, STATE_ANR);
+                if (result == RESULT_ANR_KILL) return -1;
+                if (result == RESULT_ANR_WAIT) return 1;
+                return 0;
+            }
+        }
+
+        void killGdbLocked() {
+            mGotGdbPrint = false;
+            if (mGdbProcess != null) {
+                System.out.println("Stopping gdbserver");
+                mGdbProcess.destroy();
+                mGdbProcess = null;
+            }
+            if (mGdbThread != null) {
+                mGdbThread.interrupt();
+                mGdbThread = null;
+            }
+        }
+
+        int waitControllerLocked(int pid, int state) {
+            if (mGdbPort != null) {
+                killGdbLocked();
+
+                try {
+                    System.out.println("Starting gdbserver on port " + mGdbPort);
+                    System.out.println("Do the following:");
+                    System.out.println("  adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort);
+                    System.out.println("  gdbclient app_process :" + mGdbPort);
+
+                    mGdbProcess = Runtime.getRuntime().exec(new String[] {
+                            "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid)
+                    });
+                    final InputStreamReader converter = new InputStreamReader(
+                            mGdbProcess.getInputStream());
+                    mGdbThread = new Thread() {
+                        @Override
+                        public void run() {
+                            BufferedReader in = new BufferedReader(converter);
+                            String line;
+                            int count = 0;
+                            while (true) {
+                                synchronized (MyActivityController.this) {
+                                    if (mGdbThread == null) {
+                                        return;
+                                    }
+                                    if (count == 2) {
+                                        mGotGdbPrint = true;
+                                        MyActivityController.this.notifyAll();
+                                    }
+                                }
+                                try {
+                                    line = in.readLine();
+                                    if (line == null) {
+                                        return;
+                                    }
+                                    System.out.println("GDB: " + line);
+                                    count++;
+                                } catch (IOException e) {
+                                    return;
+                                }
+                            }
+                        }
+                    };
+                    mGdbThread.start();
+
+                    // Stupid waiting for .5s.  Doesn't matter if we end early.
+                    try {
+                        this.wait(500);
+                    } catch (InterruptedException e) {
+                    }
+
+                } catch (IOException e) {
+                    System.err.println("Failure starting gdbserver: " + e);
+                    killGdbLocked();
+                }
+            }
+            mState = state;
+            System.out.println("");
+            printMessageForState();
+
+            while (mState != STATE_NORMAL) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+
+            killGdbLocked();
+
+            return mResult;
+        }
+
+        void resumeController(int result) {
+            synchronized (this) {
+                mState = STATE_NORMAL;
+                mResult = result;
+                notifyAll();
+            }
+        }
+
+        void printMessageForState() {
+            switch (mState) {
+                case STATE_NORMAL:
+                    System.out.println("Monitoring activity manager...  available commands:");
+                    break;
+                case STATE_CRASHED:
+                    System.out.println("Waiting after crash...  available commands:");
+                    System.out.println("(c)ontinue: show crash dialog");
+                    System.out.println("(k)ill: immediately kill app");
+                    break;
+                case STATE_EARLY_ANR:
+                    System.out.println("Waiting after early ANR...  available commands:");
+                    System.out.println("(c)ontinue: standard ANR processing");
+                    System.out.println("(k)ill: immediately kill app");
+                    break;
+                case STATE_ANR:
+                    System.out.println("Waiting after ANR...  available commands:");
+                    System.out.println("(c)ontinue: show ANR dialog");
+                    System.out.println("(k)ill: immediately kill app");
+                    System.out.println("(w)ait: wait some more");
+                    break;
+            }
+            System.out.println("(q)uit: finish monitoring");
+        }
+
+        void run() throws RemoteException {
+            try {
+                printMessageForState();
+
+                mAm.setActivityController(this);
+                mState = STATE_NORMAL;
+
+                InputStreamReader converter = new InputStreamReader(System.in);
+                BufferedReader in = new BufferedReader(converter);
+                String line;
+
+                while ((line = in.readLine()) != null) {
+                    boolean addNewline = true;
+                    if (line.length() <= 0) {
+                        addNewline = false;
+                    } else if ("q".equals(line) || "quit".equals(line)) {
+                        resumeController(RESULT_DEFAULT);
+                        break;
+                    } else if (mState == STATE_CRASHED) {
+                        if ("c".equals(line) || "continue".equals(line)) {
+                            resumeController(RESULT_CRASH_DIALOG);
+                        } else if ("k".equals(line) || "kill".equals(line)) {
+                            resumeController(RESULT_CRASH_KILL);
+                        } else {
+                            System.out.println("Invalid command: " + line);
+                        }
+                    } else if (mState == STATE_ANR) {
+                        if ("c".equals(line) || "continue".equals(line)) {
+                            resumeController(RESULT_ANR_DIALOG);
+                        } else if ("k".equals(line) || "kill".equals(line)) {
+                            resumeController(RESULT_ANR_KILL);
+                        } else if ("w".equals(line) || "wait".equals(line)) {
+                            resumeController(RESULT_ANR_WAIT);
+                        } else {
+                            System.out.println("Invalid command: " + line);
+                        }
+                    } else if (mState == STATE_EARLY_ANR) {
+                        if ("c".equals(line) || "continue".equals(line)) {
+                            resumeController(RESULT_EARLY_ANR_CONTINUE);
+                        } else if ("k".equals(line) || "kill".equals(line)) {
+                            resumeController(RESULT_EARLY_ANR_KILL);
+                        } else {
+                            System.out.println("Invalid command: " + line);
+                        }
+                    } else {
+                        System.out.println("Invalid command: " + line);
+                    }
+
+                    synchronized (this) {
+                        if (addNewline) {
+                            System.out.println("");
+                        }
+                        printMessageForState();
+                    }
+                }
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                mAm.setActivityController(null);
+            }
+        }
+    }
+
+    private void runMonitor() throws Exception {
+        String opt;
+        String gdbPort = null;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("--gdb")) {
+                gdbPort = nextArgRequired();
+            } else {
+                System.err.println("Error: Unknown option: " + opt);
+                showUsage();
+                return;
+            }
+        }
+
+        MyActivityController controller = new MyActivityController(gdbPort);
+        controller.run();
+    }
+
     private class IntentReceiver extends IIntentReceiver.Stub {
         private boolean mFinished = false;
 
@@ -594,6 +897,9 @@
                 "    start profiling: am profile <PROCESS> start <FILE>\n" +
                 "    stop profiling: am profile <PROCESS> stop\n" +
                 "\n" +
+                "    start monitoring: am monitor [--gdb <port>]\n" +
+                "        --gdb: start gdbserv on the given port at crash/ANR\n" +
+                "\n" +
                 "    <INTENT> specifications include these flags:\n" +
                 "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
                 "        [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 6e9caaf..822f62d 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -122,11 +122,11 @@
     run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
     run_command("LIBRANK", 10, "librank", NULL);
 
-    dump_file("BINDER FAILED TRANSACTION LOG", "/proc/binder/failed_transaction_log");
-    dump_file("BINDER TRANSACTION LOG", "/proc/binder/transaction_log");
-    dump_file("BINDER TRANSACTIONS", "/proc/binder/transactions");
-    dump_file("BINDER STATS", "/proc/binder/stats");
-    run_command("BINDER PROCESS STATE", 10, "sh", "-c", "cat /proc/binder/proc/*");
+    dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
+    dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
+    dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
+    dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
+    dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
 
     run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
 
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 01cddc6..22d5b39 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -40,6 +40,7 @@
     { AID_RADIO, "radio.simphonebook" },
 /* TODO: remove after phone services are updated: */
     { AID_RADIO, "phone" },
+    { AID_RADIO, "sip" },
     { AID_RADIO, "isms" },
     { AID_RADIO, "iphonesubinfo" },
     { AID_RADIO, "simphonebook" },
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index c424281..b718299 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -36,24 +36,30 @@
 static const int32_t kIFramesIntervalSec = 1;
 static const int32_t kVideoBitRate = 512 * 1024;
 static const int32_t kAudioBitRate = 12200;
-static const int32_t kColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
 static const int64_t kDurationUs = 10000000LL;  // 10 seconds
 
 #if 1
 class DummySource : public MediaSource {
 
 public:
-    DummySource(int width, int height)
+    DummySource(int width, int height, int colorFormat)
         : mWidth(width),
           mHeight(height),
+          mColorFormat(colorFormat),
           mSize((width * height * 3) / 2) {
         mGroup.add_buffer(new MediaBuffer(mSize));
+
+        // Check the color format to make sure
+        // that the buffer size mSize it set correctly above.
+        CHECK(colorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
+              colorFormat == OMX_COLOR_FormatYUV420Planar);
     }
 
     virtual sp<MetaData> getFormat() {
         sp<MetaData> meta = new MetaData;
         meta->setInt32(kKeyWidth, mWidth);
         meta->setInt32(kKeyHeight, mHeight);
+        meta->setInt32(kKeyColorFormat, mColorFormat);
         meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
 
         return meta;
@@ -100,6 +106,7 @@
 private:
     MediaBufferGroup mGroup;
     int mWidth, mHeight;
+    int mColorFormat;
     size_t mSize;
     int64_t mNumFramesOutput;;
 
@@ -139,20 +146,47 @@
     return source;
 }
 
+enum {
+    kYUV420SP = 0,
+    kYUV420P  = 1,
+};
+
+// returns -1 if mapping of the given color is unsuccessful
+// returns an omx color enum value otherwise
+static int translateColorToOmxEnumValue(int color) {
+    switch (color) {
+        case kYUV420SP:
+            return OMX_COLOR_FormatYUV420SemiPlanar;
+        case kYUV420P:
+            return OMX_COLOR_FormatYUV420Planar;
+        default:
+            fprintf(stderr, "Unsupported color: %d\n", color);
+            return -1;
+    }
+}
+
 int main(int argc, char **argv) {
     android::ProcessState::self()->startThreadPool();
 
     DataSource::RegisterDefaultSniffers();
 
 #if 1
-    if (argc != 2) {
-        fprintf(stderr, "usage: %s filename\n", argv[0]);
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s <filename> <input_color_format>\n", argv[0]);
+        fprintf(stderr, "       <input_color_format>:  0 (YUV420SP) or 1 (YUV420P)\n");
         return 1;
     }
 
+    int colorFormat = translateColorToOmxEnumValue(atoi(argv[2]));
+    if (colorFormat == -1) {
+        fprintf(stderr, "input color format must be 0 (YUV420SP) or 1 (YUV420P)\n");
+        return 1;
+    }
     OMXClient client;
     CHECK_EQ(client.connect(), OK);
 
+    status_t err = OK;
+
 #if 0
     sp<MediaSource> source = createSource(argv[1]);
 
@@ -173,7 +207,7 @@
 #else
     int width = 720;
     int height = 480;
-    sp<MediaSource> decoder = new DummySource(width, height);
+    sp<MediaSource> decoder = new DummySource(width, height, colorFormat);
 #endif
 
     sp<MetaData> enc_meta = new MetaData;
@@ -187,7 +221,7 @@
     enc_meta->setInt32(kKeyStride, width);
     enc_meta->setInt32(kKeySliceHeight, height);
     enc_meta->setInt32(kKeyIFramesInterval, kIFramesIntervalSec);
-    enc_meta->setInt32(kKeyColorFormat, kColorFormat);
+    enc_meta->setInt32(kKeyColorFormat, colorFormat);
 
     sp<MediaSource> encoder =
         OMXCodec::Create(
@@ -197,14 +231,14 @@
     sp<MPEG4Writer> writer = new MPEG4Writer("/sdcard/output.mp4");
     writer->addSource(encoder);
     writer->setMaxFileDuration(kDurationUs);
-    writer->start();
+    CHECK_EQ(OK, writer->start());
     while (!writer->reachedEOS()) {
         fprintf(stderr, ".");
         usleep(100000);
     }
-    writer->stop();
+    err = writer->stop();
 #else
-    encoder->start();
+    CHECK_EQ(OK, encoder->start());
 
     MediaBuffer *buffer;
     while (encoder->read(&buffer) == OK) {
@@ -222,7 +256,7 @@
         buffer = NULL;
     }
 
-    encoder->stop();
+    err = encoder->stop();
 #endif
 
     printf("$\n");
@@ -247,12 +281,16 @@
         buffer = NULL;
     }
 
-    source->stop();
+    err = source->stop();
 
     delete source;
     source = NULL;
 #endif
 
+    if (err != OK && err != ERROR_END_OF_STREAM) {
+        fprintf(stderr, "record failed: %d\n", err);
+        return 1;
+    }
     return 0;
 }
 #else
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 8ab94ad..f55b746 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "stagefright"
+#include <media/stagefright/foundation/ADebug.h>
+
 #include <sys/time.h>
 
 #include <stdlib.h>
@@ -27,10 +31,11 @@
 #include <media/IMediaPlayerService.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include "include/ARTSPController.h"
+#include "include/LiveSource.h"
+#include "include/NuCachedSource2.h"
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/JPEGSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -41,8 +46,11 @@
 #include <media/mediametadataretriever.h>
 
 #include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
 
+#include <fcntl.h>
+
 using namespace android;
 
 static long gNumRepetitions;
@@ -120,7 +128,7 @@
 
             bool shouldSeek = false;
             if (err == INFO_FORMAT_CHANGED) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
 
                 printf("format changed.\n");
                 continue;
@@ -206,7 +214,7 @@
             options.clearSeekTo();
 
             if (err != OK) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
 
                 if (err == INFO_FORMAT_CHANGED) {
                     printf("format changed.\n");
@@ -267,14 +275,120 @@
     }
 }
 
-static void writeSourceToMP4(const sp<MediaSource> &source) {
+////////////////////////////////////////////////////////////////////////////////
+
+struct DetectSyncSource : public MediaSource {
+    DetectSyncSource(const sp<MediaSource> &source);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+private:
+    enum StreamType {
+        AVC,
+        MPEG4,
+        H263,
+        OTHER,
+    };
+
+    sp<MediaSource> mSource;
+    StreamType mStreamType;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
+};
+
+DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source)
+    : mSource(source),
+      mStreamType(OTHER) {
+    const char *mime;
+    CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        mStreamType = AVC;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) {
+        mStreamType = MPEG4;
+        CHECK(!"sync frame detection not implemented yet for MPEG4");
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
+        mStreamType = H263;
+        CHECK(!"sync frame detection not implemented yet for H.263");
+    }
+}
+
+status_t DetectSyncSource::start(MetaData *params) {
+    return mSource->start(params);
+}
+
+status_t DetectSyncSource::stop() {
+    return mSource->stop();
+}
+
+sp<MetaData> DetectSyncSource::getFormat() {
+    return mSource->getFormat();
+}
+
+static bool isIDRFrame(MediaBuffer *buffer) {
+    const uint8_t *data =
+        (const uint8_t *)buffer->data() + buffer->range_offset();
+    size_t size = buffer->range_length();
+    for (size_t i = 0; i + 3 < size; ++i) {
+        if (!memcmp("\x00\x00\x01", &data[i], 3)) {
+            uint8_t nalType = data[i + 3] & 0x1f;
+            if (nalType == 5) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+status_t DetectSyncSource::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    status_t err = mSource->read(buffer, options);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (mStreamType == AVC && isIDRFrame(*buffer)) {
+        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
+    } else {
+        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+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
 
-    CHECK_EQ(writer->addSource(source), OK);
+    // at most one minute.
+    writer->setMaxFileDuration(60000000ll);
+
+    for (size_t i = 0; i < sources.size(); ++i) {
+        sp<MediaSource> source = sources.editItemAt(i);
+
+        CHECK_EQ(writer->addSource(
+                    syncInfoPresent ? source : new DetectSyncSource(source)),
+                (status_t)OK);
+    }
 
     sp<MetaData> params = new MetaData;
-    CHECK_EQ(writer->start(), OK);
+    params->setInt32(kKeyNotRealTime, true);
+    CHECK_EQ(writer->start(params.get()), (status_t)OK);
 
     while (!writer->reachedEOS()) {
         usleep(100000);
@@ -283,7 +397,7 @@
 }
 
 static void performSeekTest(const sp<MediaSource> &source) {
-    CHECK_EQ(OK, source->start());
+    CHECK_EQ((status_t)OK, source->start());
 
     int64_t durationUs;
     CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
@@ -335,7 +449,7 @@
         }
     }
 
-    CHECK_EQ(OK, source->stop());
+    CHECK_EQ((status_t)OK, source->stop());
 }
 
 static void usage(const char *me) {
@@ -481,10 +595,10 @@
         for (int k = 0; k < argc; ++k) {
             const char *filename = argv[k];
 
-            CHECK_EQ(retriever->setDataSource(filename), OK);
+            CHECK_EQ(retriever->setDataSource(filename), (status_t)OK);
             CHECK_EQ(retriever->setMode(
                         METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL),
-                     OK);
+                     (status_t)OK);
 
             sp<IMemory> mem = retriever->captureFrame();
 
@@ -530,7 +644,7 @@
             Vector<CodecCapabilities> results;
             CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
                                  true, // queryDecoders
-                                 &results), OK);
+                                 &results), (status_t)OK);
 
             for (size_t i = 0; i < results.size(); ++i) {
                 printf("  decoder '%s' supports ",
@@ -579,12 +693,16 @@
     status_t err = client.connect();
 
     for (int k = 0; k < argc; ++k) {
+        bool syncInfoPresent = true;
+
         const char *filename = argv[k];
 
         sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
 
-        if ((strncasecmp(filename, "sine:", 5)
-                && strncasecmp(filename, "rtsp://", 7)) && dataSource == NULL) {
+        if (strncasecmp(filename, "sine:", 5)
+                && strncasecmp(filename, "rtsp://", 7)
+                && strncasecmp(filename, "httplive://", 11)
+                && dataSource == NULL) {
             fprintf(stderr, "Unable to create data source.\n");
             return 1;
         }
@@ -596,10 +714,14 @@
             isJPEG = true;
         }
 
+        Vector<sp<MediaSource> > mediaSources;
         sp<MediaSource> mediaSource;
 
         if (isJPEG) {
             mediaSource = new JPEGSource(dataSource);
+            if (gWriteMP4) {
+                mediaSources.push(mediaSource);
+            }
         } else if (!strncasecmp("sine:", filename, 5)) {
             char *end;
             long sampleRate = strtol(filename + 5, &end, 10);
@@ -608,6 +730,9 @@
                 sampleRate = 44100;
             }
             mediaSource = new SineSource(sampleRate, 1);
+            if (gWriteMP4) {
+                mediaSources.push(mediaSource);
+            }
         } else {
             sp<MediaExtractor> extractor;
 
@@ -625,6 +750,20 @@
                 }
 
                 extractor = rtspController.get();
+
+                syncInfoPresent = false;
+            } else if (!strncasecmp("httplive://", filename, 11)) {
+                String8 uri("http://");
+                uri.append(filename + 11);
+
+                dataSource = new LiveSource(uri.string());
+                dataSource = new NuCachedSource2(dataSource);
+
+                extractor =
+                    MediaExtractor::Create(
+                            dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+
+                syncInfoPresent = false;
             } else {
                 extractor = MediaExtractor::Create(dataSource);
                 if (extractor == NULL) {
@@ -635,46 +774,75 @@
 
             size_t numTracks = extractor->countTracks();
 
-            sp<MetaData> meta;
-            size_t i;
-            for (i = 0; i < numTracks; ++i) {
-                meta = extractor->getTrackMetaData(
-                        i, MediaExtractor::kIncludeExtensiveMetaData);
+            if (gWriteMP4) {
+                bool haveAudio = false;
+                bool haveVideo = false;
+                for (size_t i = 0; i < numTracks; ++i) {
+                    sp<MediaSource> source = extractor->getTrack(i);
 
-                const char *mime;
-                meta->findCString(kKeyMIMEType, &mime);
+                    const char *mime;
+                    CHECK(source->getFormat()->findCString(
+                                kKeyMIMEType, &mime));
 
-                if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
-                    break;
+                    bool useTrack = false;
+                    if (!haveAudio && !strncasecmp("audio/", mime, 6)) {
+                        haveAudio = true;
+                        useTrack = true;
+                    } else if (!haveVideo && !strncasecmp("video/", mime, 6)) {
+                        haveVideo = true;
+                        useTrack = true;
+                    }
+
+                    if (useTrack) {
+                        mediaSources.push(source);
+
+                        if (haveAudio && haveVideo) {
+                            break;
+                        }
+                    }
+                }
+            } else {
+                sp<MetaData> meta;
+                size_t i;
+                for (i = 0; i < numTracks; ++i) {
+                    meta = extractor->getTrackMetaData(
+                            i, MediaExtractor::kIncludeExtensiveMetaData);
+
+                    const char *mime;
+                    meta->findCString(kKeyMIMEType, &mime);
+
+                    if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+                        break;
+                    }
+
+                    if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
+                        break;
+                    }
+
+                    meta = NULL;
                 }
 
-                if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
-                    break;
+                if (meta == NULL) {
+                    fprintf(stderr,
+                            "No suitable %s track found. The '-a' option will "
+                            "target audio tracks only, the default is to target "
+                            "video tracks only.\n",
+                            audioOnly ? "audio" : "video");
+                    return -1;
                 }
 
-                meta = NULL;
-            }
+                int64_t thumbTimeUs;
+                if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
+                    printf("thumbnailTime: %lld us (%.2f secs)\n",
+                           thumbTimeUs, thumbTimeUs / 1E6);
+                }
 
-            if (meta == NULL) {
-                fprintf(stderr,
-                        "No suitable %s track found. The '-a' option will "
-                        "target audio tracks only, the default is to target "
-                        "video tracks only.\n",
-                        audioOnly ? "audio" : "video");
-                return -1;
+                mediaSource = extractor->getTrack(i);
             }
-
-            int64_t thumbTimeUs;
-            if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
-                printf("thumbnailTime: %lld us (%.2f secs)\n",
-                       thumbTimeUs, thumbTimeUs / 1E6);
-            }
-
-            mediaSource = extractor->getTrack(i);
         }
 
         if (gWriteMP4) {
-            writeSourceToMP4(mediaSource);
+            writeSourcesToMP4(mediaSources, syncInfoPresent);
         } else if (seekTest) {
             performSeekTest(mediaSource);
         } else {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6e6e86f..72bf825 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1427,7 +1427,9 @@
      * <li> The function will be called between {@link #onStop} and
      * {@link #onDestroy}.
      * <li> A new instance of the activity will <em>always</em> be immediately
-     * created after this one's {@link #onDestroy()} is called.
+     * created after this one's {@link #onDestroy()} is called.  In particular,
+     * <em>no</em> messages will be dispatched during this time (when the returned
+     * object does not have an activity to be associated with).
      * <li> The object you return here will <em>always</em> be available from
      * the {@link #getLastNonConfigurationInstance()} method of the following
      * activity instance as described there.
@@ -1440,6 +1442,15 @@
      * may change based on the configuration, including any data loaded from
      * resources such as strings, layouts, or drawables.
      * 
+     * <p>The guarantee of no message handling during the switch to the next
+     * activity simplifies use with active objects.  For example if your retained
+     * state is an {@link android.os.AsyncTask} you are guaranteed that its
+     * call back functions (like {@link android.os.AsyncTask#onPostExecute}) will
+     * not be called from the call here until you execute the next instance's
+     * {@link #onCreate(Bundle)}.  (Note however that there is of course no such
+     * guarantee for {@link android.os.AsyncTask#doInBackground} since that is
+     * running in a separate thread.)
+     *
      * @return Return any Object holding the desired state to propagate to the
      * next activity instance.
      */
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a180837..89812ab 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1272,6 +1272,15 @@
             return true;
         }
 
+        case GET_PROVIDER_MIME_TYPE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            String type = getProviderMimeType(uri);
+            reply.writeNoException();
+            reply.writeString(type);
+            return true;
+        }
+
         case NEW_URI_PERMISSION_OWNER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String name = data.readString();
@@ -2847,6 +2856,20 @@
         reply.recycle();
     }
 
+    public String getProviderMimeType(Uri uri)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        uri.writeToParcel(data, 0);
+        mRemote.transact(GET_PROVIDER_MIME_TYPE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        String res = reply.readString();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     public IBinder newUriPermissionOwner(String name)
             throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d8b5253..a10a823 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -60,6 +60,7 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.LogPrinter;
 import android.util.Slog;
 import android.view.Display;
 import android.view.View;
@@ -118,6 +119,7 @@
     private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
     private static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+    static final boolean DEBUG_MESSAGES = false;
     static final boolean DEBUG_BROADCAST = false;
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = false;
@@ -363,7 +365,8 @@
         private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%17s %8d";
         private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d";
-        private static final String DB_INFO_FORMAT = "  %8d %8d %10d  %s";
+        private static final String TWO_COUNT_COLUMNS_DB = "%20s %8d %20s %8d";
+        private static final String DB_INFO_FORMAT = "  %8d %8d %14d  %s";
 
         // Formatting for checkin service - update version if row format changes
         private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
@@ -806,15 +809,15 @@
             // SQLite mem info
             pw.println(" ");
             pw.println(" SQL");
-            printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "memoryUsed:",
+            printRow(pw, TWO_COUNT_COLUMNS_DB, "heap:", sqliteAllocated, "MEMORY_USED:",
                     stats.memoryUsed / 1024);
-            printRow(pw, TWO_COUNT_COLUMNS, "pageCacheOverflo:", stats.pageCacheOverflo / 1024,
-                    "largestMemAlloc:", stats.largestMemAlloc / 1024);
+            printRow(pw, TWO_COUNT_COLUMNS_DB, "PAGECACHE_OVERFLOW:",
+                    stats.pageCacheOverflo / 1024, "MALLOC_SIZE:", stats.largestMemAlloc / 1024);
             pw.println(" ");
             int N = stats.dbStats.size();
             if (N > 0) {
                 pw.println(" DATABASES");
-                printRow(pw, "  %8s %8s %10s  %s", "Pagesize", "Dbsize", "Lookaside", "Dbname");
+                printRow(pw, "  %8s %8s %14s  %s", "pgsz", "dbsz", "Lookaside(b)", "Dbname");
                 for (int i = 0; i < N; i++) {
                     DbStats dbStats = stats.dbStats.get(i);
                     printRow(pw, DB_INFO_FORMAT, dbStats.pageSize, dbStats.dbSize,
@@ -873,7 +876,7 @@
         public static final int DISPATCH_PACKAGE_BROADCAST = 133;
         public static final int SCHEDULE_CRASH          = 134;
         String codeToString(int code) {
-            if (localLOGV) {
+            if (DEBUG_MESSAGES) {
                 switch (code) {
                     case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
                     case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
@@ -915,6 +918,7 @@
             return "(unknown)";
         }
         public void handleMessage(Message msg) {
+            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
             switch (msg.what) {
                 case LAUNCH_ACTIVITY: {
                     ActivityClientRecord r = (ActivityClientRecord)msg.obj;
@@ -1036,6 +1040,7 @@
                 case SCHEDULE_CRASH:
                     throw new RemoteServiceException((String)msg.obj);
             }
+            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
 
         void maybeSnapshot() {
@@ -1483,7 +1488,7 @@
 
     private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
         synchronized (this) {
-            if (localLOGV) Slog.v(
+            if (DEBUG_MESSAGES) Slog.v(
                 TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
                 + ": " + arg1 + " / " + obj);
             Message msg = Message.obtain();
@@ -3282,12 +3287,20 @@
         }
     }
 
-    private final IContentProvider getProvider(Context context, String name) {
+    private final IContentProvider getExistingProvider(Context context, String name) {
         synchronized(mProviderMap) {
             final ProviderClientRecord pr = mProviderMap.get(name);
             if (pr != null) {
                 return pr.mProvider;
             }
+            return null;
+        }
+    }
+
+    private final IContentProvider getProvider(Context context, String name) {
+        IContentProvider existing = getExistingProvider(context, name);
+        if (existing != null) {
+            return existing;
         }
 
         IActivityManager.ContentProviderHolder holder = null;
@@ -3332,6 +3345,22 @@
         return provider;
     }
 
+    public final IContentProvider acquireExistingProvider(Context c, String name) {
+        IContentProvider provider = getExistingProvider(c, name);
+        if(provider == null)
+            return null;
+        IBinder jBinder = provider.asBinder();
+        synchronized(mProviderMap) {
+            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
+            if(prc == null) {
+                mProviderRefCountMap.put(jBinder, new ProviderRefCount(1));
+            } else {
+                prc.count++;
+            } //end else
+        } //end synchronized
+        return provider;
+    }
+
     public final boolean releaseProvider(IContentProvider provider) {
         if(provider == null) {
             return false;
@@ -3340,7 +3369,7 @@
         synchronized(mProviderMap) {
             ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
             if(prc == null) {
-                if(localLOGV) Slog.v(TAG, "releaseProvider::Weird shouldnt be here");
+                if(localLOGV) Slog.v(TAG, "releaseProvider::Weird shouldn't be here");
                 return false;
             } else {
                 prc.count--;
@@ -3607,6 +3636,11 @@
         ActivityThread thread = new ActivityThread();
         thread.attach(false);
 
+        if (false) {
+            Looper.myLooper().setMessageLogging(new
+                    LogPrinter(Log.DEBUG, "ActivityThread"));
+        }
+
         Looper.loop();
 
         if (Process.supportsProcesses()) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 09ef710..c01516f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -67,7 +67,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;
@@ -1626,22 +1625,23 @@
     // ----------------------------------------------------------------------
 
     private static final class ApplicationContentResolver extends ContentResolver {
-        public ApplicationContentResolver(Context context,
-                                          ActivityThread mainThread)
-        {
+        public ApplicationContentResolver(Context context, ActivityThread mainThread) {
             super(context);
             mMainThread = mainThread;
         }
 
         @Override
-        protected IContentProvider acquireProvider(Context context, String name)
-        {
+        protected IContentProvider acquireProvider(Context context, String name) {
             return mMainThread.acquireProvider(context, name);
         }
 
         @Override
-        public boolean releaseProvider(IContentProvider provider)
-        {
+        protected IContentProvider acquireExistingProvider(Context context, String name) {
+            return mMainThread.acquireExistingProvider(context, name);
+        }
+
+        @Override
+        public boolean releaseProvider(IContentProvider provider) {
             return mMainThread.releaseProvider(provider);
         }
         
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/app/DownloadManager.java
similarity index 85%
rename from core/java/android/net/DownloadManager.java
rename to core/java/android/app/DownloadManager.java
index 5320da3..c6fef4f 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -14,25 +14,28 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
+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;
 import android.provider.Downloads;
+import android.util.Pair;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -195,6 +198,12 @@
     public final static int ERROR_CANNOT_RESUME = 1008;
 
     /**
+     * Value of {@link #COLUMN_ERROR_CODE} when the requested destination file already exists (the
+     * download manager will not overwrite an existing file).
+     */
+    public final static int ERROR_FILE_ALREADY_EXISTS = 1009;
+
+    /**
      * Broadcast intent action sent by the download manager when a download completes.
      */
     public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
@@ -207,6 +216,11 @@
             "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
 
     /**
+     * Intent action to launch an activity to display all downloads.
+     */
+    public final static String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS";
+
+    /**
      * Intent extra included with {@link #ACTION_DOWNLOAD_COMPLETE} intents, indicating the ID (as a
      * long) of the download that just completed.
      */
@@ -235,10 +249,12 @@
         Downloads.COLUMN_URI,
         Downloads.COLUMN_MIME_TYPE,
         Downloads.COLUMN_TOTAL_BYTES,
-        Downloads._DATA,
         Downloads.COLUMN_STATUS,
         Downloads.COLUMN_CURRENT_BYTES,
         Downloads.COLUMN_LAST_MODIFICATION,
+        Downloads.COLUMN_DESTINATION,
+        Downloads.Impl.COLUMN_FILE_NAME_HINT,
+        Downloads.Impl._DATA,
     };
 
     private static final Set<String> LONG_COLUMNS = new HashSet<String>(
@@ -246,8 +262,12 @@
                           COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP));
 
     /**
-     * This class contains all the information necessary to request a new download.  The URI is the
+     * This class contains all the information necessary to request a new download. The URI is the
      * only required parameter.
+     *
+     * Note that the default download destination is a shared volume where the system might delete
+     * your file if it needs to reclaim space for system use. If this is a problem, use a location
+     * on external storage (see {@link #setDestinationUri(Uri)}.
      */
     public static class Request {
         /**
@@ -262,19 +282,13 @@
          */
         public static final int NETWORK_WIFI = 1 << 1;
 
-        /**
-         * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
-         * {@link ConnectivityManager#TYPE_WIMAX}.
-         */
-        public static final int NETWORK_WIMAX = 1 << 2;
-
         private Uri mUri;
         private Uri mDestinationUri;
-        private Map<String, String> mRequestHeaders = new HashMap<String, String>();
-        private String mTitle;
-        private String mDescription;
+        private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
+        private CharSequence mTitle;
+        private CharSequence mDescription;
         private boolean mShowNotification = true;
-        private String mMediaType;
+        private String mMimeType;
         private boolean mRoamingAllowed = true;
         private int mAllowedNetworkTypes = ~0; // default to all network types allowed
         private boolean mIsVisibleInDownloadsUi = true;
@@ -294,12 +308,12 @@
         }
 
         /**
-         * Set the local destination for the downloaded data. Must be a file URI to a path on
+         * Set the local destination for the downloaded file. Must be a file URI to a path on
          * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
          * permission.
          *
-         *  By default, downloads are saved to a generated file in the download cache and may be
-         * deleted by the download manager at any time.
+         * By default, downloads are saved to a generated filename in the shared download cache and
+         * may be deleted by the system at any time to reclaim space.
          *
          * @return this object
          */
@@ -309,13 +323,62 @@
         }
 
         /**
-         * Set an HTTP header to be included with the download request.
+         * Set the local destination for the downloaded file to a path within the application's
+         * external files directory (as returned by {@link Context#getExternalFilesDir(String)}.
+         *
+         * @param context the {@link Context} to use in determining the external files directory
+         * @param dirType the directory type to pass to {@link Context#getExternalFilesDir(String)}
+         * @param subPath the path within the external directory, including the destination filename
+         * @return this object
+         */
+        public Request setDestinationInExternalFilesDir(Context context, String dirType,
+                String subPath) {
+            setDestinationFromBase(context.getExternalFilesDir(dirType), subPath);
+            return this;
+        }
+
+        /**
+         * Set the local destination for the downloaded file to a path within the public external
+         * storage directory (as returned by
+         * {@link Environment#getExternalStoragePublicDirectory(String)}.
+         *
+         * @param dirType the directory type to pass to
+         *        {@link Environment#getExternalStoragePublicDirectory(String)}
+         * @param subPath the path within the external directory, including the destination filename
+         * @return this object
+         */
+        public Request setDestinationInExternalPublicDir(String dirType, String subPath) {
+            setDestinationFromBase(Environment.getExternalStoragePublicDirectory(dirType), subPath);
+            return this;
+        }
+
+        private void setDestinationFromBase(File base, String subPath) {
+            if (subPath == null) {
+                throw new NullPointerException("subPath cannot be null");
+            }
+            mDestinationUri = Uri.withAppendedPath(Uri.fromFile(base), subPath);
+        }
+
+        /**
+         * Add an HTTP header to be included with the download request.  The header will be added to
+         * the end of the list.
          * @param header HTTP header name
          * @param value header value
          * @return this object
+         * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2">HTTP/1.1
+         *      Message Headers</a>
          */
-        public Request setRequestHeader(String header, String value) {
-            mRequestHeaders.put(header, value);
+        public Request addRequestHeader(String header, String value) {
+            if (header == null) {
+                throw new NullPointerException("header cannot be null");
+            }
+            if (header.contains(":")) {
+                throw new IllegalArgumentException("header may not contain ':'");
+            }
+            if (value == null) {
+                value = "";
+            }
+            mRequestHeaders.add(Pair.create(header, value));
             return this;
         }
 
@@ -323,7 +386,7 @@
          * Set the title of this download, to be displayed in notifications (if enabled)
          * @return this object
          */
-        public Request setTitle(String title) {
+        public Request setTitle(CharSequence title) {
             mTitle = title;
             return this;
         }
@@ -332,19 +395,20 @@
          * Set a description of this download, to be displayed in notifications (if enabled)
          * @return this object
          */
-        public Request setDescription(String description) {
+        public Request setDescription(CharSequence description) {
             mDescription = description;
             return this;
         }
 
         /**
-         * Set the Internet Media Type of this download.  This will override the media type declared
+         * Set the MIME content type of this download.  This will override the content type declared
          * in the server's response.
-         * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
+         * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP/1.1
+         *      Media Types</a>
          * @return this object
          */
-        public Request setMediaType(String mediaType) {
-            mMediaType = mediaType;
+        public Request setMimeType(String mimeType) {
+            mMimeType = mimeType;
             return this;
         }
 
@@ -359,7 +423,6 @@
          *
          * @param show whether the download manager should show a notification for this download.
          * @return this object
-         * @hide
          */
         public Request setShowRunningNotification(boolean show) {
             mShowNotification = show;
@@ -423,7 +486,7 @@
 
             putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
             putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
-            putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMediaType);
+            putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMimeType);
 
             values.put(Downloads.COLUMN_VISIBILITY,
                     mShowNotification ? Downloads.VISIBILITY_VISIBLE
@@ -438,16 +501,16 @@
 
         private void encodeHttpHeaders(ContentValues values) {
             int index = 0;
-            for (Map.Entry<String, String> entry : mRequestHeaders.entrySet()) {
-                String headerString = entry.getKey() + ": " + entry.getValue();
+            for (Pair<String, String> header : mRequestHeaders) {
+                String headerString = header.first + ": " + header.second;
                 values.put(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX + index, headerString);
                 index++;
             }
         }
 
-        private void putIfNonNull(ContentValues contentValues, String key, String value) {
+        private void putIfNonNull(ContentValues contentValues, String key, Object value) {
             if (value != null) {
-                contentValues.put(key, value);
+                contentValues.put(key, value.toString());
             }
         }
     }
@@ -820,15 +883,15 @@
         }
 
         private String getLocalUri() {
-            String localUri = getUnderlyingString(Downloads.Impl._DATA);
-            if (localUri == null) {
-                return null;
-            }
-
             long destinationType = getUnderlyingLong(Downloads.Impl.COLUMN_DESTINATION);
             if (destinationType == Downloads.Impl.DESTINATION_FILE_URI) {
-                // return file URI for external download
-                return Uri.fromFile(new File(localUri)).toString();
+                // return client-provided file URI for external download
+                return getUnderlyingString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
+            }
+
+            if (destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
+                // return stored destination for legacy external download
+                return Uri.fromFile(new File(getUnderlyingString(Downloads.Impl._DATA))).toString();
             }
 
             // return content URI for cache download
@@ -894,6 +957,9 @@
                 case Downloads.Impl.STATUS_CANNOT_RESUME:
                     return ERROR_CANNOT_RESUME;
 
+                case Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR:
+                    return ERROR_FILE_ALREADY_EXISTS;
+
                 default:
                     return ERROR_UNKNOWN;
             }
diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl
index c76a517..aca8305 100644
--- a/core/java/android/app/IActivityController.aidl
+++ b/core/java/android/app/IActivityController.aidl
@@ -48,6 +48,11 @@
             long timeMillis, String stackTrace);
     
     /**
+     * Early call as soon as an ANR is detected.
+     */
+    int appEarlyNotResponding(String processName, int pid, String annotation);
+
+    /**
      * An application process is not responding.  Return 0 to show the "app
      * not responding" dialog, 1 to continue waiting, or -1 to kill it
      * immediately.
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f9bd461..28af0d3 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -313,6 +313,8 @@
 
     public void crashApplication(int uid, int initialPid, String packageName,
             String message) throws RemoteException;
+
+    public String getProviderMimeType(Uri uri) throws RemoteException;
     
     public IBinder newUriPermissionOwner(String name) throws RemoteException;
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
@@ -526,7 +528,8 @@
     int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
     int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
     int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113;
-    int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
-    int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
-    int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
+    int GET_PROVIDER_MIME_TYPE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
+    int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
+    int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
+    int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
 }
diff --git a/core/java/android/app/backup/package.html b/core/java/android/app/backup/package.html
index ae29994..e140349 100644
--- a/core/java/android/app/backup/package.html
+++ b/core/java/android/app/backup/package.html
@@ -3,6 +3,9 @@
 <p>Contains the backup and restore functionality available to
 applications. If a user wipes the data on their device or upgrades to a new Android-powered
 device, all applications that have enabled backup will restore the user's previous data.</p>
+
+<p>For a detailed guide to using the backup APIs, see the <a
+href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p>
 {@more}
 
 <p>All backup and restore operations are controlled by the {@link
@@ -22,8 +25,5 @@
   <li>Restore the data saved to remote storage</li>
 </ul>
 
-<p>For a detailed guide to using the backup APIs, see the <a
-href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p>
-
 </BODY>
 </HTML>
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index dc4e9c4..1d6e8b8 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -544,6 +544,12 @@
      * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
      * Processes and Threads</a>.
      *
+     * <p>Note that there are no permissions needed for an application to
+     * access this information; if your content provider requires read and/or
+     * write permissions, or is not exported, all applications can still call
+     * this method regardless of their access permissions.  This allows them
+     * to retrieve the MIME type for a URI when dispatching intents.
+     *
      * @param uri the URI to query.
      * @return a MIME type string, or null if there is no type.
      */
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 69f7611..81ff414 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.accounts.Account;
+import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -176,6 +177,12 @@
 
     /** @hide */
     protected abstract IContentProvider acquireProvider(Context c, String name);
+    /** Providing a default implementation of this, to avoid having to change
+     * a lot of other things, but implementations of ContentResolver should
+     * implement it. @hide */
+    protected IContentProvider acquireExistingProvider(Context c, String name) {
+        return acquireProvider(c, name);
+    }
     /** @hide */
     public abstract boolean releaseProvider(IContentProvider icp);
 
@@ -186,20 +193,29 @@
      * using the content:// scheme.
      * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
      */
-    public final String getType(Uri url)
-    {
-        IContentProvider provider = acquireProvider(url);
-        if (provider == null) {
+    public final String getType(Uri url) {
+        IContentProvider provider = acquireExistingProvider(url);
+        if (provider != null) {
+            try {
+                return provider.getType(url);
+            } catch (RemoteException e) {
+                return null;
+            } catch (java.lang.Exception e) {
+                return null;
+            } finally {
+                releaseProvider(provider);
+            }
+        }
+
+        if (!SCHEME_CONTENT.equals(url.getScheme())) {
             return null;
         }
+
         try {
-            return provider.getType(url);
+            String type = ActivityManagerNative.getDefault().getProviderMimeType(url);
+            return type;
         } catch (RemoteException e) {
             return null;
-        } catch (java.lang.Exception e) {
-            return null;
-        } finally {
-            releaseProvider(provider);
         }
     }
 
@@ -717,14 +733,13 @@
     }
 
     /**
-     * Returns the content provider for the given content URI..
+     * Returns the content provider for the given content URI.
      *
      * @param uri The URI to a content provider
      * @return The ContentProvider for the given URI, or null if no content provider is found.
      * @hide
      */
-    public final IContentProvider acquireProvider(Uri uri)
-    {
+    public final IContentProvider acquireProvider(Uri uri) {
         if (!SCHEME_CONTENT.equals(uri.getScheme())) {
             return null;
         }
@@ -736,6 +751,25 @@
     }
 
     /**
+     * Returns the content provider for the given content URI if the process
+     * already has a reference on it.
+     *
+     * @param uri The URI to a content provider
+     * @return The ContentProvider for the given URI, or null if no content provider is found.
+     * @hide
+     */
+    public final IContentProvider acquireExistingProvider(Uri uri) {
+        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+            return null;
+        }
+        String auth = uri.getAuthority();
+        if (auth != null) {
+            return acquireExistingProvider(mContext, uri.getAuthority());
+        }
+        return null;
+    }
+
+    /**
      * @hide
      */
     public final IContentProvider acquireProvider(String name) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0100550..0dd2e4a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1207,7 +1207,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
@@ -1256,7 +1256,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);
 
@@ -1539,7 +1539,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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4bd9bd9..cb6b708 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -636,6 +636,15 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's audio pipeline is low-latency,
+     * more suitable for audio applications sensitive to delays or lag in
+     * sound input or output.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * other devices via Bluetooth.
      */
@@ -713,6 +722,21 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes an accelerometer.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a barometer (air
+     * pressure sensor.)
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device includes a magnetometer (compass).
      */
     @SdkConstant(SdkConstantType.FEATURE)
@@ -720,10 +744,10 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
-     * {@link #hasSystemFeature}: The device includes an accelerometer.
+     * {@link #hasSystemFeature}: The device includes a gyroscope.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
+    public static final String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e20cb5e..e853120 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -39,6 +39,7 @@
 
 import com.android.internal.util.XmlUtils;
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -336,7 +337,7 @@
         try {
             // We must read the stream for the JarEntry to retrieve
             // its certificates.
-            InputStream is = jarFile.getInputStream(je);
+            InputStream is = new BufferedInputStream(jarFile.getInputStream(je));
             while (is.read(readBuffer, 0, readBuffer.length) != -1) {
                 // not using
             }
@@ -1252,8 +1253,7 @@
                 "<permission-group>", sa,
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
-                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
-                com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo)) {
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon, 0)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -1288,8 +1288,7 @@
                 "<permission>", sa,
                 com.android.internal.R.styleable.AndroidManifestPermission_name,
                 com.android.internal.R.styleable.AndroidManifestPermission_label,
-                com.android.internal.R.styleable.AndroidManifestPermission_icon,
-                com.android.internal.R.styleable.AndroidManifestPermission_logo)) {
+                com.android.internal.R.styleable.AndroidManifestPermission_icon, 0)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -1342,8 +1341,7 @@
                 "<permission-tree>", sa,
                 com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
                 com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
-                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
-                com.android.internal.R.styleable.AndroidManifestPermissionTree_logo)) {
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon, 0)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -1387,8 +1385,7 @@
             mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
                     com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
-                    com.android.internal.R.styleable.AndroidManifestInstrumentation_icon,
-                    com.android.internal.R.styleable.AndroidManifestInstrumentation_logo);
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_icon, 0);
             mParseInstrumentationArgs.tag = "<instrumentation>";
         }
         
@@ -1500,8 +1497,6 @@
 
         ai.icon = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
-        ai.logo = sa.getResourceId(
-                com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
         ai.theme = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
         ai.descriptionRes = sa.getResourceId(
@@ -1761,11 +1756,6 @@
             outInfo.nonLocalizedLabel = null;
         }
         
-        int logoVal = sa.getResourceId(logoRes, 0);
-        if (logoVal != 0) {
-            outInfo.logo = logoVal;
-        }
-
         TypedValue v = sa.peekValue(labelRes);
         if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
             outInfo.nonLocalizedLabel = v.coerceToString();
@@ -1786,8 +1776,7 @@
             mParseActivityArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestActivity_name,
                     com.android.internal.R.styleable.AndroidManifestActivity_label,
-                    com.android.internal.R.styleable.AndroidManifestActivity_icon,
-                    com.android.internal.R.styleable.AndroidManifestActivity_logo,
+                    com.android.internal.R.styleable.AndroidManifestActivity_icon, 0,
                     mSeparateProcesses,
                     com.android.internal.R.styleable.AndroidManifestActivity_process,
                     com.android.internal.R.styleable.AndroidManifestActivity_description,
@@ -1997,8 +1986,7 @@
             mParseActivityAliasArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestActivityAlias_name,
                     com.android.internal.R.styleable.AndroidManifestActivityAlias_label,
-                    com.android.internal.R.styleable.AndroidManifestActivityAlias_icon,
-                    com.android.internal.R.styleable.AndroidManifestActivityAlias_logo,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, 0,
                     mSeparateProcesses,
                     0,
                     com.android.internal.R.styleable.AndroidManifestActivityAlias_description,
@@ -2126,8 +2114,7 @@
             mParseProviderArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestProvider_name,
                     com.android.internal.R.styleable.AndroidManifestProvider_label,
-                    com.android.internal.R.styleable.AndroidManifestProvider_icon,
-                    com.android.internal.R.styleable.AndroidManifestProvider_logo,
+                    com.android.internal.R.styleable.AndroidManifestProvider_icon, 0,
                     mSeparateProcesses,
                     com.android.internal.R.styleable.AndroidManifestProvider_process,
                     com.android.internal.R.styleable.AndroidManifestProvider_description,
@@ -2399,8 +2386,7 @@
             mParseServiceArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestService_name,
                     com.android.internal.R.styleable.AndroidManifestService_label,
-                    com.android.internal.R.styleable.AndroidManifestService_icon,
-                    com.android.internal.R.styleable.AndroidManifestService_logo,
+                    com.android.internal.R.styleable.AndroidManifestService_icon, 0,
                     mSeparateProcesses,
                     com.android.internal.R.styleable.AndroidManifestService_process,
                     com.android.internal.R.styleable.AndroidManifestService_description,
@@ -2614,9 +2600,6 @@
         outInfo.icon = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
         
-        outInfo.logo = sa.getResourceId(
-                com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0);
-
         sa.recycle();
 
         int outerDepth = parser.getDepth();
@@ -2884,11 +2867,6 @@
                 outInfo.nonLocalizedLabel = null;
             }
             
-            int logoVal = args.sa.getResourceId(args.logoRes, 0);
-            if (logoVal != 0) {
-                outInfo.logo = logoVal;
-            }
-
             TypedValue v = args.sa.peekValue(args.labelRes);
             if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
                 outInfo.nonLocalizedLabel = v.coerceToString();
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 89c3f96..b2a166b 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -134,7 +134,7 @@
 
         public DbStats(String dbName, long pageCount, long pageSize, int lookaside) {
             this.dbName = dbName;
-            this.pageSize = pageSize;
+            this.pageSize = pageSize / 1024;
             dbSize = (pageCount * pageSize) / 1024;
             this.lookaside = lookaside;
         }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 26600f3..0d8228c 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1182,14 +1182,17 @@
         public static final String FOCUS_MODE_EDOF = "edof";
 
         /**
-         * Continuous auto focus mode. The camera continuously tries to focus.
-         * This is ideal for shooting video or shooting photo of moving object.
-         * Auto focus starts when the parameter is set. Applications should not
-         * call {@link #autoFocus(AutoFocusCallback)} in this mode. To stop
-         * continuous focus, applications should change the focus mode to other
-         * modes.
+         * Continuous auto focus mode intended for video recording. The camera
+         * continuously tries to focus. This is ideal for shooting video.
+         * Applications still can call {@link
+         * #takePicture(Camera.ShutterCallback, Camera.PictureCallback,
+         * Camera.PictureCallback)} in this mode but the subject may not be in
+         * focus. Auto focus starts when the parameter is set. Applications
+         * should not call {@link #autoFocus(AutoFocusCallback)} in this mode.
+         * To stop continuous focus, applications should change the focus mode
+         * to other modes.
          */
-        public static final String FOCUS_MODE_CONTINUOUS = "continuous";
+        public static final String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video";
 
         // Indices for focus distance array.
         /**
@@ -2023,7 +2026,7 @@
          * @see #FOCUS_MODE_MACRO
          * @see #FOCUS_MODE_FIXED
          * @see #FOCUS_MODE_EDOF
-         * @see #FOCUS_MODE_CONTINUOUS
+         * @see #FOCUS_MODE_CONTINUOUS_VIDEO
          */
         public String getFocusMode() {
             return get(KEY_FOCUS_MODE);
@@ -2225,8 +2228,8 @@
          * #autoFocus(AutoFocusCallback)}, {@link #cancelAutoFocus}, or {@link
          * #startPreview()}. Applications can call {@link #getParameters()}
          * and this method anytime to get the latest focus distances. If the
-         * focus mode is FOCUS_MODE_CONTINUOUS, focus distances may change from
-         * time to time.
+         * focus mode is FOCUS_MODE_CONTINUOUS_VIDEO, focus distances may change
+         * from time to time.
          *
          * This method is intended to estimate the distance between the camera
          * and the subject. After autofocus, the subject distance may be within
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index 4039a69..3ee8a80 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -70,8 +70,11 @@
         if (!implCreated) {
             synchronized (this) {
                 if (!implCreated) {
-                    implCreated = true;
-                    impl.create(true);
+                    try {
+                        impl.create(true);
+                    } finally {
+                        implCreated = true;
+                    }
                 }
             }
         }
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index c5a3277..7803bf2 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -50,6 +50,12 @@
     private static boolean DBG = true;
     private static final String TAG = "NetworkStateTracker";
 
+    // Share the event space with ConnectivityService (which we can't see, but
+    // must send events to).  If you change these, change ConnectivityService
+    // too.
+    private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+    private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+
     public static final int EVENT_STATE_CHANGED = 1;
     public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
     /**
@@ -61,16 +67,6 @@
     public static final int EVENT_CONFIGURATION_CHANGED = 4;
     public static final int EVENT_ROAMING_CHANGED = 5;
     public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
-    public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
-    /**
-     * arg1: network type
-     * arg2: condition (0 bad, 100 good)
-     */
-    public static final int EVENT_INET_CONDITION_CHANGE = 8;
-    /**
-     * arg1: network type
-     */
-    public static final int EVENT_INET_CONDITION_HOLD_END = 9;
 
     public NetworkStateTracker(Context context,
             Handler target,
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1e88c56..d49c8be 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -301,7 +301,11 @@
          */
         public static abstract class Proc {
 
-            public static class ExcessiveWake {
+            public static class ExcessivePower {
+                public static final int TYPE_WAKE = 1;
+                public static final int TYPE_CPU = 2;
+
+                public int type;
                 public long overTime;
                 public long usedTime;
             }
@@ -343,9 +347,9 @@
              */
             public abstract long getTimeAtCpuSpeedStep(int speedStep, int which);
 
-            public abstract int countExcessiveWakes();
+            public abstract int countExcessivePowers();
 
-            public abstract ExcessiveWake getExcessiveWake(int i);
+            public abstract ExcessivePower getExcessivePower(int i);
         }
 
         /**
@@ -397,7 +401,7 @@
         }
     }
 
-    public final class HistoryItem implements Parcelable {
+    public final static class HistoryItem implements Parcelable {
         public HistoryItem next;
         
         public long time;
@@ -482,6 +486,18 @@
             dest.writeInt(states);
         }
         
+        public void setTo(HistoryItem o) {
+            time = o.time;
+            cmd = o.cmd;
+            batteryLevel = o.batteryLevel;
+            batteryStatus = o.batteryStatus;
+            batteryHealth = o.batteryHealth;
+            batteryPlugType = o.batteryPlugType;
+            batteryTemperature = o.batteryTemperature;
+            batteryVoltage = o.batteryVoltage;
+            states = o.states;
+        }
+
         public void setTo(long time, byte cmd, HistoryItem o) {
             this.time = time;
             this.cmd = cmd;
@@ -526,6 +542,10 @@
         }
     }
     
+    public abstract boolean startIteratingHistoryLocked();
+
+    public abstract boolean getNextHistoryLocked(HistoryItem out);
+
     /**
      * Return the current history of battery state changes.
      */
@@ -1577,7 +1597,7 @@
                     systemTime = ps.getSystemTime(which);
                     starts = ps.getStarts(which);
                     numExcessive = which == STATS_SINCE_CHARGED
-                            ? ps.countExcessiveWakes() : 0;
+                            ? ps.countExcessivePowers() : 0;
 
                     if (userTime != 0 || systemTime != 0 || starts != 0
                             || numExcessive != 0) {
@@ -1593,9 +1613,17 @@
                         }
                         pw.println(sb.toString());
                         for (int e=0; e<numExcessive; e++) {
-                            Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
+                            Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e);
                             if (ew != null) {
-                                pw.print(prefix); pw.print("      * Killed for wake lock use: ");
+                                pw.print(prefix); pw.print("      * Killed for ");
+                                        if (ew.type == Uid.Proc.ExcessivePower.TYPE_WAKE) {
+                                            pw.print("wake lock");
+                                        } else if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) {
+                                            pw.print("cpu");
+                                        } else {
+                                            pw.print("unknown");
+                                        }
+                                        pw.print(" use: ");
                                         TimeUtils.formatDuration(ew.usedTime, pw);
                                         pw.print(" over ");
                                         TimeUtils.formatDuration(ew.overTime, pw);
@@ -1688,8 +1716,8 @@
      */
     @SuppressWarnings("unused")
     public void dumpLocked(PrintWriter pw) {
-        HistoryItem rec = getHistory();
-        if (rec != null) {
+        final HistoryItem rec = new HistoryItem();
+        if (startIteratingHistoryLocked()) {
             pw.println("Battery History:");
             long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
             int oldState = 0;
@@ -1698,7 +1726,7 @@
             int oldPlug = -1;
             int oldTemp = -1;
             int oldVolt = -1;
-            while (rec != null) {
+            while (getNextHistoryLocked(rec)) {
                 pw.print("  ");
                 TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
                 pw.print(" ");
@@ -1803,7 +1831,6 @@
                     pw.println();
                 }
                 oldState = rec.states;
-                rec = rec.next;
             }
             pw.println("");
         }
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 2813863..6237c0e 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -33,6 +33,7 @@
 public class MessageQueue {
     Message mMessages;
     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
+    private IdleHandler[] mPendingIdleHandlers;
     private boolean mQuiting = false;
     boolean mQuitAllowed = true;
 
@@ -105,90 +106,74 @@
     }
 
     final Message next() {
-        boolean tryIdle = true;
-        // when we start out, we'll just touch the input pipes and then go from there
-        int timeToNextEventMillis = 0;
+        int pendingIdleHandlerCount = -1; // -1 only during first iteration
+        int nextPollTimeoutMillis = 0;
 
-        while (true) {
-            long now;
-            Object[] idlers = null;
+        for (;;) {
+            if (nextPollTimeoutMillis != 0) {
+                Binder.flushPendingCommands();
+            }
+            nativePollOnce(nextPollTimeoutMillis);
 
-            boolean dispatched = nativePollOnce(timeToNextEventMillis);
-
-            // Try to retrieve the next message, returning if found.
             synchronized (this) {
-                now = SystemClock.uptimeMillis();
-                Message msg = pullNextLocked(now);
+                // Try to retrieve the next message.  Return if found.
+                final long now = SystemClock.uptimeMillis();
+                final Message msg = mMessages;
                 if (msg != null) {
-                    return msg;
-                }
-                
-                if (tryIdle && mIdleHandlers.size() > 0) {
-                    idlers = mIdleHandlers.toArray();
-                }
-            }
-    
-            // There was no message so we are going to wait...  but first,
-            // if there are any idle handlers let them know.
-            boolean didIdle = false;
-            if (idlers != null) {
-                for (Object idler : idlers) {
-                    boolean keep = false;
-                    try {
-                        didIdle = true;
-                        keep = ((IdleHandler)idler).queueIdle();
-                    } catch (Throwable t) {
-                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
-                    }
-
-                    if (!keep) {
-                        synchronized (this) {
-                            mIdleHandlers.remove(idler);
-                        }
-                    }
-                }
-            }
-            
-            // While calling an idle handler, a new message could have been
-            // delivered...  so go back and look again for a pending message.
-            if (didIdle) {
-                tryIdle = false;
-                continue;
-            }
-
-            synchronized (this) {
-                // No messages, nobody to tell about it...  time to wait!
-                if (mMessages != null) {
-                    long longTimeToNextEventMillis = mMessages.when - now;
-                    
-                    if (longTimeToNextEventMillis > 0) {
-                        Binder.flushPendingCommands();
-                        timeToNextEventMillis = (int) Math.min(longTimeToNextEventMillis,
-                                Integer.MAX_VALUE);
+                    final long when = msg.when;
+                    if (now >= when) {
+                        mMessages = msg.next;
+                        if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
+                        return msg;
                     } else {
-                        timeToNextEventMillis = 0;
+                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                     }
                 } else {
-                    Binder.flushPendingCommands();
-                    timeToNextEventMillis = -1;
+                    nextPollTimeoutMillis = -1;
+                }
+
+                // If first time, then get the number of idlers to run.
+                if (pendingIdleHandlerCount < 0) {
+                    pendingIdleHandlerCount = mIdleHandlers.size();
+                }
+                if (pendingIdleHandlerCount == 0) {
+                    // No idle handlers to run.  Loop and wait some more.
+                    continue;
+                }
+
+                if (mPendingIdleHandlers == null) {
+                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
+                }
+                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
+            }
+
+            // Run the idle handlers.
+            // We only ever reach this code block during the first iteration.
+            for (int i = 0; i < pendingIdleHandlerCount; i++) {
+                final IdleHandler idler = mPendingIdleHandlers[i];
+                mPendingIdleHandlers[i] = null; // release the reference to the handler
+
+                boolean keep = false;
+                try {
+                    keep = idler.queueIdle();
+                } catch (Throwable t) {
+                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
+                }
+
+                if (!keep) {
+                    synchronized (this) {
+                        mIdleHandlers.remove(idler);
+                    }
                 }
             }
-            // loop to the while(true) and do the appropriate nativeWait(when)
-        }
-    }
 
-    final Message pullNextLocked(long now) {
-        Message msg = mMessages;
-        if (msg != null) {
-            if (now >= msg.when) {
-                mMessages = msg.next;
-                if (Config.LOGV) Log.v(
-                    "MessageQueue", "Returning message: " + msg);
-                return msg;
-            }
-        }
+            // Reset the idle handler count to 0 so we do not run them again.
+            pendingIdleHandlerCount = 0;
 
-        return null;
+            // While calling an idle handler, a new message could have been delivered
+            // so go back and look again for a pending message without waiting.
+            nextPollTimeoutMillis = 0;
+        }
     }
 
     final boolean enqueueMessage(Message msg, long when) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3876a3e..156da47 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -232,6 +232,8 @@
         /**
          * Sets whether this WakeLock is ref counted.
          *
+         * <p>Wake locks are reference counted by default.
+         *
          * @param value true for ref counted, false for not ref counted.
          */
         public void setReferenceCounted(boolean value)
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 5fea6fe..e56e257 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -307,8 +307,10 @@
      * Requires the {@link android.Manifest.permission#REBOOT} permission.
      *
      * @param context      the Context to use
-     * @param packageFile  the update package to install.  Currently
-     * must be on the /cache or /data partitions.
+     * @param packageFile  the update package to install.  Must be on
+     * a partition mountable by recovery.  (The set of partitions
+     * known to recovery may vary from device to device.  Generally,
+     * /cache and /data are safe.)
      *
      * @throws IOException  if writing the recovery command file
      * fails, or if the reboot itself fails.
@@ -316,15 +318,6 @@
     public static void installPackage(Context context, File packageFile)
         throws IOException {
         String filename = packageFile.getCanonicalPath();
-
-        if (filename.startsWith("/cache/")) {
-            filename = "CACHE:" + filename.substring(7);
-        } else if (filename.startsWith("/data/")) {
-            filename = "DATA:" + filename.substring(6);
-        } else {
-            throw new IllegalArgumentException(
-                "Must start with /cache or /data: " + filename);
-        }
         Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
         String arg = "--update_package=" + filename;
         bootCommand(context, arg);
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f571c42..3ddaad9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -30,8 +30,59 @@
 import java.util.HashMap;
 
 /**
- * <p>StrictMode lets you impose stricter rules under which your
- * application runs.</p>
+ * <p>StrictMode is a developer tool which lets you impose stricter
+ * rules under which your application runs.
+ *
+ * <p>StrictMode is most commonly used to catch accidental disk or
+ * network access on the application's main thread, where UI
+ * operations are received and animations take place.  Keeping disk
+ * and network operations off the main thread makes for much smoother,
+ * more responsive applications.
+ *
+ * <p class="note">Note that even though an Android device's disk is
+ * often on flash memory, many devices run a filesystem on top of that
+ * memory with very limited concurrency.  It's often the case that
+ * almost all disk accesses are fast, but may in individual cases be
+ * dramatically slower when certain I/O is happening in the background
+ * from other processes.  If possible, it's best to assume that such
+ * things are not fast.</p>
+ *
+ * <p>Example code to enable from early in your
+ * {@link android.app.Application}, {@link android.app.Activity}, or
+ * other application component's
+ * {@link android.app.Application#onCreate} method:
+ *
+ * <pre>
+ * public void onCreate() {
+ *     if (DEVELOPER_MODE) {
+ *         StrictMode.setThreadPolicy(StrictMode.DISALLOW_DISK_WRITE |
+ *                 StrictMode.DISALLOW_DISK_READ |
+ *                 StrictMode.DISALLOW_NETWORK |
+ *                 StrictMode.PENALTY_LOG);
+ *     }
+ *     super.onCreate();
+ * }
+ * </pre>
+ *
+ * <p>Then you can watch the output of <code>adb logcat</code> while you
+ * use your application.
+ *
+ * <p>If you find violations that you feel are problematic, there are
+ * a variety of tools to help solve them: threads, {@link android.os.Handler},
+ * {@link android.os.AsyncTask}, {@link android.app.IntentService}, etc.
+ * But don't feel compelled to fix everything that StrictMode finds.  In particular,
+ * a lot of disk accesses are often necessary during the normal activity lifecycle.  Use
+ * StrictMode to find things you did on accident.  Network requests on the UI thread
+ * are almost always a problem, though.
+ *
+ * <p class="note">StrictMode is not a security mechanism and is not
+ * guaranteed to find all disk or network accesses.  While it does
+ * propagate its state across process boundaries when doing
+ * {@link android.os.Binder} calls, it's still ultimately a best
+ * effort mechanism.  Notably, disk or network access from JNI calls
+ * won't necessarily trigger it.  Future versions of Android may catch
+ * more (or fewer) operations, so you should never leave StrictMode
+ * enabled in shipping applications on the Android Market.
  */
 public final class StrictMode {
     private static final String TAG = "StrictMode";
@@ -45,8 +96,22 @@
 
     private StrictMode() {}
 
+    /**
+     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
+     * thread to write to disk.
+     */
     public static final int DISALLOW_DISK_WRITE = 0x01;
+
+    /**
+     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
+     * thread to read from disk.
+     */
     public static final int DISALLOW_DISK_READ = 0x02;
+
+    /**
+     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
+     * thread to access the network.
+     */
     public static final int DISALLOW_NETWORK = 0x04;
 
     /** @hide */
@@ -54,23 +119,30 @@
             DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
 
     /**
-     * Flag to log to the system log.
+     * Penalty flag for {@link #setThreadPolicy} to log violations to
+     * the system log, visible with <code>adb logcat</code>.
      */
     public static final int PENALTY_LOG = 0x10;  // normal android.util.Log
 
     /**
-     * Show an annoying dialog to the user.  Will be rate-limited to be only
-     * a little annoying.
+     * Penalty flag for {@link #setThreadPolicy} to show an annoying
+     * dialog to the developer, rate-limited to be only a little
+     * annoying.
      */
     public static final int PENALTY_DIALOG = 0x20;
 
     /**
-     * Crash hard if policy is violated.
+     * Penalty flag for {@link #setThreadPolicy} to crash hard if
+     * policy is violated.
      */
     public static final int PENALTY_DEATH = 0x40;
 
     /**
-     * Log a stacktrace to the DropBox on policy violation.
+     * Penalty flag for {@link #setThreadPolicy} to log a stacktrace
+     * and timing data to the
+     * {@link android.os.DropBoxManager DropBox} on policy violation.
+     * Intended mostly for platform integrators doing beta user field
+     * data collection.
      */
     public static final int PENALTY_DROPBOX = 0x80;
 
@@ -109,10 +181,17 @@
     };
 
     /**
-     * Sets the policy for what actions the current thread is denied,
-     * as well as the penalty for violating the policy.
+     * Sets the policy for what actions the current thread isn't
+     * expected to do, as well as the penalty if it does.
      *
-     * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
+     * <p>Internally this sets a thread-local integer which is
+     * propagated across cross-process IPC calls, meaning you can
+     * catch violations when a system service or another process
+     * accesses the disk or network on your behalf.
+     *
+     * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values,
+     *     e.g. {@link #DISALLOW_DISK_READ}, {@link #DISALLOW_DISK_WRITE},
+     *     {@link #DISALLOW_NETWORK}, {@link #PENALTY_LOG}.
      */
     public static void setThreadPolicy(final int policyMask) {
         // In addition to the Java-level thread-local in Dalvik's
@@ -160,7 +239,7 @@
     }
 
     /**
-     * Returns the bitmask of the current thread's blocking policy.
+     * Returns the bitmask of the current thread's policy.
      *
      * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
      */
@@ -169,8 +248,10 @@
     }
 
     /**
-     * Updates the current thread's policy mask to allow reading &amp;
-     * writing to disk.
+     * A convenience wrapper around {@link #getThreadPolicy} and
+     * {@link #setThreadPolicy}.  Updates the current thread's policy
+     * mask to allow both reading &amp; writing to disk, returning the
+     * old policy so you can restore it at the end of a block.
      *
      * @return the old policy mask, to be passed to setThreadPolicy to
      *         restore the policy.
@@ -185,8 +266,10 @@
     }
 
     /**
-     * Updates the current thread's policy mask to allow reading from
-     * disk.
+     * A convenience wrapper around {@link #getThreadPolicy} and
+     * {@link #setThreadPolicy}.  Updates the current thread's policy
+     * mask to allow reading from disk, returning the old
+     * policy so you can restore it at the end of a block.
      *
      * @return the old policy mask, to be passed to setThreadPolicy to
      *         restore the policy.
diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java
index 635323e..f5e1bac 100644
--- a/core/java/android/pim/RecurrenceSet.java
+++ b/core/java/android/pim/RecurrenceSet.java
@@ -181,7 +181,9 @@
         boolean inUtc = start.parse(dtstart);
         boolean allDay = start.allDay;
 
-        if (inUtc) {
+        // We force TimeZone to UTC for "all day recurring events" as the server is sending no
+        // TimeZone in DTSTART for them
+        if (inUtc || allDay) {
             tzid = Time.TIMEZONE_UTC;
         }
                 
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
index 94f7c5f..02e1674 100644
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ b/core/java/android/pim/vcard/VCardEntry.java
@@ -20,12 +20,10 @@
 import android.content.ContentProviderResult;
 import android.content.ContentResolver;
 import android.content.OperationApplicationException;
+import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -38,6 +36,10 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.util.Log;
@@ -59,6 +61,9 @@
 
     private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
 
+    private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
+    private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
+
     private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
 
     static {
@@ -1111,6 +1116,23 @@
         if (mAccount != null) {
             builder.withValue(RawContacts.ACCOUNT_NAME, mAccount.name);
             builder.withValue(RawContacts.ACCOUNT_TYPE, mAccount.type);
+
+            // Assume that caller side creates this group if it does not exist.
+            if (ACCOUNT_TYPE_GOOGLE.equals(mAccount.type)) {
+                final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] {
+                        Groups.SOURCE_ID },
+                        Groups.TITLE + "=?", new String[] {
+                        GOOGLE_MY_CONTACTS_GROUP }, null);
+                try {
+                    if (cursor != null && cursor.moveToFirst()) {
+                        myGroupsId = cursor.getString(0);
+                    }
+                } finally {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                }
+            }
         } else {
             builder.withValue(RawContacts.ACCOUNT_NAME, null);
             builder.withValue(RawContacts.ACCOUNT_TYPE, null);
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
new file mode 100644
index 0000000..b93dfd8
--- /dev/null
+++ b/core/java/android/provider/AlarmClock.java
@@ -0,0 +1,71 @@
+/*
+ * 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.provider;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+
+/**
+ * The AlarmClock provider contains an Intent action and extras that can be used
+ * to start an Activity to set a new alarm in an alarm clock application.
+ *
+ * Applications that wish to receive the ACTION_SET_ALARM Intent should create
+ * an activity to handle the Intent that requires the permission
+ * com.android.alarm.permission.SET_ALARM.  Applications that wish to create a
+ * new alarm should use
+ * {@link android.content.Context#startActivity Context.startActivity()} so that
+ * the user has the option of choosing which alarm clock application to use.
+ */
+public final class AlarmClock {
+    /**
+     * Activity Action: Set an alarm.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
+
+    /**
+     * Activity Extra: Provide a custom message for the alarm.
+     * <p>
+     * This can be passed as an extra field in the Intent created with
+     * ACTION_SET_ALARM.
+     */
+    public static final String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE";
+
+    /**
+     * Activity Extra: The hour of the alarm being set.
+     * <p>
+     * This value can be passed as an extra field to the Intent created with
+     * ACTION_SET_ALARM.  If it is not provided, the behavior is undefined and
+     * is up to the application.  The value is an integer and ranges from 0 to
+     * 23.
+     */
+    public static final String EXTRA_HOUR = "android.intent.extra.alarm.HOUR";
+
+    /**
+     * Activity Extra: The minutes of the alarm being set.
+     * <p>
+     * This value can be passed as an extra field to the Intent created with
+     * ACTION_SET_ALARM.  If it is not provided, the behavior is undefined and
+     * is up to the application.  The value is an integer and ranges from 0 to
+     * 59.
+     */
+    public static final String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES";
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 09cc3e0..7484964 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -412,6 +412,7 @@
          * Contact Chat Capabilities. See {@link StatusUpdates} for individual
          * definitions.
          * <p>Type: NUMBER</p>
+         * @hide
          */
         public static final String CONTACT_CHAT_CAPABILITY = "contact_chat_capability";
 
@@ -1961,23 +1962,27 @@
         /**
          * Contact's audio/video chat capability level.
          * <P>Type: INTEGER (one of the values below)</P>
+         * @hide
          */
         public static final String CHAT_CAPABILITY = "chat_capability";
 
         /**
          * An allowed value of {@link #CHAT_CAPABILITY}. Indicates that the contact's device can
          * display a video feed.
+         * @hide
          */
         public static final int CAPABILITY_HAS_VIDEO_PLAYBACK_ONLY = 1;
 
         /**
          * An allowed value of {@link #CHAT_CAPABILITY}. Indicates audio-chat capability.
+         * @hide
          */
         public static final int CAPABILITY_HAS_VOICE = 2;
 
         /**
          * An allowed value of {@link #CHAT_CAPABILITY}. Indicates that the contact's device has a
          * camera that can be used for video chat (e.g. a front-facing camera on a phone).
+         * @hide
          */
         public static final int CAPABILITY_HAS_CAMERA = 4;
     }
@@ -3119,31 +3124,6 @@
      * <li>{@link #AVAILABLE}</li>
      * </ul>
      * </p>
-     * <p>
-     * Since presence status is inherently volatile, the content provider
-     * may choose not to store this field in long-term storage.
-     * </p>
-     * </td>
-     * </tr>
-     * <tr>
-     * <td>int</td>
-     * <td>{@link #CHAT_CAPABILITY}</td>
-     * <td>read/write</td>
-     * <td>Contact IM chat compatibility value. The allowed values are:
-     * <p>
-     * <ul>
-     * <li>{@link #CAPABILITY_HAS_VIDEO_PLAYBACK_ONLY}</li>
-     * <li>{@link #CAPABILITY_HAS_VOICE}</li>
-     * <li>{@link #CAPABILITY_HAS_CAMERA}</li>
-     * </ul>
-     * </p>
-     * <p>
-     * Since chat compatibility is inherently volatile as the contact's availability moves from
-     * one device to another, the content provider may choose not to store this field in long-term
-     * storage.
-     * </p>
-     * </td>
-     * </tr>
      * <tr>
      * <td>String</td>
      * <td>{@link #STATUS}</td>
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 74c7372..1e358c9 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -895,6 +895,14 @@
          */
         public static final String COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI = "is_visible_in_downloads_ui";
 
+        /**
+         * If true, the user has confirmed that this download can proceed over the mobile network
+         * even though it exceeds the recommended maximum size.
+         * <P>Type: BOOLEAN</P>
+         */
+        public static final String COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT =
+            "bypass_recommended_size_limit";
+
         /*
          * Lists the destinations that an application can specify for a download.
          */
@@ -1077,7 +1085,12 @@
         /**
          * The lowest-valued error status that is not an actual HTTP status code.
          */
-        public static final int MIN_ARTIFICIAL_ERROR_STATUS = 489;
+        public static final int MIN_ARTIFICIAL_ERROR_STATUS = 488;
+
+        /**
+         * The requested destination file already exists.
+         */
+        public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
 
         /**
          * Some possibly transient error occurred, but we can't resume the download.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fd60115..1dd2420 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -355,6 +355,20 @@
             "android.settings.MANAGE_APPLICATIONS_SETTINGS";
 
     /**
+     * Activity Action: Show settings to manage all applications.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS =
+            "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
+
+    /**
      * Activity Action: Show screen of details about a particular application.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -3428,6 +3442,15 @@
                 "download_manager_max_bytes_over_mobile";
 
         /**
+         * The recommended maximum size, in bytes, of a download that the download manager should
+         * transfer over a non-wifi connection. Over this size, the use will be warned, but will
+         * have the option to start the download over the mobile connection anyway.
+         * @hide
+         */
+        public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
+                "download_manager_recommended_max_bytes_over_mobile";
+
+        /**
          * ms during which to consume extra events related to Inet connection condition
          * after a transtion to fully-connected
          * @hide
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 0408673..220e023 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -23,19 +23,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.TextView;
-import android.widget.TextView.CursorController;
 
 // XXX this doesn't extend MetaKeyKeyListener because the signatures
 // don't match.  Need to figure that out.  Meanwhile the meta keys
 // won't work in fields that don't take input.
 
 public class ArrowKeyMovementMethod implements MovementMethod {
-    /**
-     * An optional controller for the cursor.
-     * Use {@link #setCursorController(CursorController)} to set this field.
-     */
-    private CursorController mCursorController;
-
     private boolean isCap(Spannable buffer) {
         return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
                 (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
@@ -192,21 +185,10 @@
     }
 
     public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) {
-        if (mCursorController != null) {
-            mCursorController.hide();
-        }
         return false;
     }
 
     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
-        if (mCursorController != null) {
-            return onTouchEventCursor(widget, buffer, event);
-        } else {
-            return onTouchEventStandard(widget, buffer, event);
-        }
-    }
-
-    private boolean onTouchEventStandard(TextView widget, Spannable buffer, MotionEvent event) {
         int initialScrollX = -1, initialScrollY = -1;
         if (event.getAction() == MotionEvent.ACTION_UP) {
             initialScrollX = Touch.getInitialScrollX(widget, buffer);
@@ -278,47 +260,6 @@
         return handled;
     }
 
-    private boolean onTouchEventCursor(TextView widget, Spannable buffer, MotionEvent event) {
-        if (widget.isFocused() && !widget.didTouchFocusSelect()) {
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_MOVE:
-                    widget.cancelLongPress();
-
-                    // Offset the current touch position (from controller to cursor)
-                    final float x = event.getX() + mCursorController.getOffsetX();
-                    final float y = event.getY() + mCursorController.getOffsetY();
-                    mCursorController.updatePosition((int) x, (int) y);
-                    return true;
-
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    mCursorController = null;
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Defines the cursor controller.
-     *
-     * When set, this object can be used to handle touch events, that can be translated into cursor
-     * updates.
-     *
-     * {@link MotionEvent#ACTION_MOVE} events will call back the 
-     * {@link CursorController#updatePosition(int, int)} controller's method, passing the current
-     * finger coordinates (offset by {@link CursorController#getOffsetX()} and
-     * {@link CursorController#getOffsetY()}) as parameters. 
-     *
-     * When the gesture is finished (on a {@link MotionEvent#ACTION_UP} or
-     * {@link MotionEvent#ACTION_CANCEL} event), the controller is reset to null.
-     *
-     * @param cursorController A cursor controller implementation
-     */
-    public void setCursorController(CursorController cursorController) {
-        mCursorController = cursorController;
-    }
-
     public boolean canSelectArbitrarily() {
         return true;
     }
diff --git a/core/java/android/util/Base64InputStream.java b/core/java/android/util/Base64InputStream.java
index da3911d..e9dac24 100644
--- a/core/java/android/util/Base64InputStream.java
+++ b/core/java/android/util/Base64InputStream.java
@@ -112,7 +112,7 @@
         if (outputStart >= outputEnd) {
             return -1;
         } else {
-            return coder.output[outputStart++];
+            return coder.output[outputStart++] & 0xff;
         }
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6e395c8..b794a6a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1551,12 +1551,6 @@
     private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
 
     /**
-     * Indicates that this view has a visible/touchable overlay.
-     * @hide
-     */
-    static final int HAS_OVERLAY = 0x10000000;
-
-    /**
      * Always allow a user to overscroll this view, provided it is a
      * view that can scroll.
      *
@@ -2843,57 +2837,6 @@
         resetPressedState();
     }
 
-    /**
-     * Enable or disable drawing overlays after a full drawing pass. This enables a view to
-     * draw on a topmost overlay layer after normal drawing completes and get right of first
-     * refusal for touch events in the window.
-     * 
-     * <em>Warning:</em> Views that use this feature should take care to disable/enable overlay
-     * appropriately when they are attached/detached from their window. All overlays should be
-     * disabled when detached.
-     * 
-     * @param enabled true if overlay drawing should be enabled for this view, false otherwise
-     * 
-     * @see #onDrawOverlay(Canvas)
-     * 
-     * @hide
-     */
-    protected void setOverlayEnabled(boolean enabled) {
-        final boolean oldValue = (mPrivateFlags & HAS_OVERLAY) == HAS_OVERLAY;
-        mPrivateFlags = (mPrivateFlags & ~HAS_OVERLAY) | (enabled ? HAS_OVERLAY : 0);
-        if (enabled != oldValue) {
-            final ViewParent parent = getParent();
-            if (parent != null) {
-                try {
-                    parent.childOverlayStateChanged(this);
-                } catch (AbstractMethodError e) {
-                    Log.e(VIEW_LOG_TAG, "Could not propagate hasOverlay state", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * @return true if this View has an overlay enabled.
-     * 
-     * @see #setOverlayEnabled(boolean)
-     * @see #onDrawOverlay(Canvas)
-     * 
-     * @hide
-     */
-    public boolean isOverlayEnabled() {
-        return (mPrivateFlags & HAS_OVERLAY) == HAS_OVERLAY;
-    }
-
-    /**
-     * Override this method to draw on an overlay layer above all other views in the window
-     * after the standard drawing pass is complete. This allows a view to draw outside its
-     * normal boundaries.
-     * @hide
-     */
-    public void onDrawOverlay(Canvas canvas) {
-    }
-
     private void resetPressedState() {
         if ((mViewFlags & ENABLED_MASK) == DISABLED) {
             return;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index deba70c..b9864ba 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -228,11 +228,6 @@
     protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
 
     /**
-     * When set, at least one child of this ViewGroup will return true from hasOverlay.
-     */
-    private static final int FLAG_CHILD_HAS_OVERLAY = 0x100000;
-
-    /**
      * Indicates which types of drawing caches are to be kept in memory.
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -860,33 +855,6 @@
                 final View[] children = mChildren;
                 final int count = mChildrenCount;
 
-                // Check for children with overlays first. They don't rely on hit rects to determine
-                // if they can accept a new touch event.
-                if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY) {
-                    for (int i = count - 1; i >= 0; i--) {
-                        final View child = children[i];
-                        // Don't let children respond to events as an overlay during an animation.
-                        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
-                                && child.getAnimation() == null
-                                && child.isOverlayEnabled()) {
-                            // offset the event to the view's coordinate system
-                            final float xc = scrolledXFloat - child.mLeft;
-                            final float yc = scrolledYFloat - child.mTop;
-                            ev.setLocation(xc, yc);
-                            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
-                            if (child.dispatchTouchEvent(ev))  {
-                                // Event handled, we have a target now.
-                                mMotionTarget = child;
-                                return true;
-                            }
-                            // The event didn't get handled, try the next view.
-                            // Don't reset the event's location, it's not
-                            // necessary here.
-                        }
-                    }
-                }
-
-                // Now check views normally.
                 for (int i = count - 1; i >= 0; i--) {
                     final View child = children[i];
                     if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
@@ -2345,8 +2313,6 @@
         if (clearChildFocus != null) {
             clearChildFocus(clearChildFocus);
         }
-
-        mGroupFlags &= ~FLAG_CHILD_HAS_OVERLAY;
     }
 
     /**
@@ -2569,8 +2535,7 @@
                 final int left = mLeft;
                 final int top = mTop;
 
-                if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY ||
-                        dirty.intersect(0, 0, mRight - left, mBottom - top) ||
+                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
                         (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
                     mPrivateFlags &= ~DRAWING_CACHE_VALID;
 
@@ -3489,69 +3454,6 @@
     }
 
     /**
-     * Called when a child's overlay state changes between enabled/disabled.
-     * @param child Child view whose state has changed or null
-     * @hide
-     */
-    public void childOverlayStateChanged(View child) {
-        boolean childHasOverlay = false;
-        if (child != null) {
-            childHasOverlay = child.isOverlayEnabled();
-        } else {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                if (childHasOverlay |= getChildAt(i).isOverlayEnabled()) {
-                    break;
-                }
-            }
-        }
-        
-        final boolean hasChildWithOverlay = childHasOverlay ||
-                (mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY;
-
-        final boolean oldValue = isOverlayEnabled();
-        mGroupFlags = (mGroupFlags & ~FLAG_CHILD_HAS_OVERLAY) |
-                (hasChildWithOverlay ? FLAG_CHILD_HAS_OVERLAY : 0);
-        if (isOverlayEnabled() != oldValue) {
-            final ViewParent parent = getParent();
-            if (parent != null) {
-                try {
-                    parent.childOverlayStateChanged(this);
-                } catch (AbstractMethodError e) {
-                    Log.e("ViewGroup", "Could not propagate hasOverlay state", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public boolean isOverlayEnabled() {
-        return super.isOverlayEnabled() ||
-                ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public void onDrawOverlay(Canvas canvas) {
-        if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY) {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                if (child.isOverlayEnabled()) {
-                    canvas.translate(child.mLeft - child.mScrollX, child.mTop - child.mScrollY);
-                    child.onDrawOverlay(canvas);
-                    canvas.translate(-(child.mLeft - child.mScrollX),
-                            -(child.mTop - child.mScrollY));
-                }
-            }
-        }
-    }
-
-    /**
      * LayoutParams are used by views to tell their parents how they want to be
      * laid out. See
      * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index a0d3618..b456c5d 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -208,11 +208,4 @@
      */
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
             boolean immediate);
-
-    /**
-     * Called when a child view's overlay state changes between enabled/disabled.
-     * @param child Child view whose state changed or null.
-     * @hide
-     */
-    public void childOverlayStateChanged(View child);
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index acec476..9c249ce 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -221,8 +221,6 @@
     AudioManager mAudioManager;
 
     private final int mDensity;
-    
-    private boolean mHasOverlay;
 
     public static IWindowSession getWindowSession(Looper mainLooper) {
         synchronized (mStaticInit) {
@@ -1520,9 +1518,6 @@
                         canvas.setScreenDensity(scalingRequired
                                 ? DisplayMetrics.DENSITY_DEVICE : 0);
                         mView.draw(canvas);
-                        if (mHasOverlay) {
-                            mView.onDrawOverlay(canvas);
-                        }
                     } finally {
                         mAttachInfo.mIgnoreDirtyState = false;
                         canvas.restoreToCount(saveCount);
@@ -1881,6 +1876,9 @@
                 deliverPointerEvent(event);
             } finally {
                 event.recycle();
+                if (msg.arg1 != 0) {
+                    finishInputEvent();
+                }
                 if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
             }
         } break;
@@ -1890,6 +1888,9 @@
                 deliverTrackballEvent(event);
             } finally {
                 event.recycle();
+                if (msg.arg1 != 0) {
+                    finishInputEvent();
+                }
             }
         } break;
         case DISPATCH_APP_VISIBILITY:
@@ -2024,15 +2025,24 @@
         }
     }
     
-    private void finishKeyEvent(KeyEvent event) {
-        if (LOCAL_LOGV) Log.v(TAG, "Telling window manager key is finished");
+    private void startInputEvent(Runnable finishedCallback) {
+        if (mFinishedCallback != null) {
+            Slog.w(TAG, "Received a new input event from the input queue but there is "
+                    + "already an unfinished input event in progress.");
+        }
+
+        mFinishedCallback = finishedCallback;
+    }
+
+    private void finishInputEvent() {
+        if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
 
         if (mFinishedCallback != null) {
             mFinishedCallback.run();
             mFinishedCallback = null;
         } else {
-            Slog.w(TAG, "Attempted to tell the input queue that the current key event "
-                    + "is finished but there is no key event actually in progress.");
+            Slog.w(TAG, "Attempted to tell the input queue that the current input event "
+                    + "is finished but there is no input event actually in progress.");
         }
     }
     
@@ -2492,7 +2502,7 @@
                 ? mView.dispatchKeyEventPreIme(event) : true;
         if (handled) {
             if (sendDone) {
-                finishKeyEvent(event);
+                finishInputEvent();
             }
             return;
         }
@@ -2523,7 +2533,7 @@
                 deliverKeyEventToViewHierarchy(event, sendDone);
                 return;
             } else if (sendDone) {
-                finishKeyEvent(event);
+                finishInputEvent();
             } else {
                 Log.w(TAG, "handleFinishedEvent(seq=" + seq
                         + " handled=" + handled + " ev=" + event
@@ -2596,7 +2606,7 @@
 
         } finally {
             if (sendDone) {
-                finishKeyEvent(event);
+                finishInputEvent();
             }
             // Let the exception fall through -- the looper will catch
             // it and take care of the bad app for us.
@@ -2627,6 +2637,7 @@
             if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
         }
         mPendingConfiguration.seq = 0;
+        //Log.d(TAG, ">>>>>> CALLING relayout");
         int relayoutResult = sWindowSession.relayout(
                 mWindow, params,
                 (int) (mView.mMeasuredWidth * appScale + 0.5f),
@@ -2634,6 +2645,7 @@
                 viewVisibility, insetsPending, mWinFrame,
                 mPendingContentInsets, mPendingVisibleInsets,
                 mPendingConfiguration, mSurface);
+        //Log.d(TAG, "<<<<<< BACK FROM relayout");
         if (restore) {
             params.restore();
         }
@@ -2779,20 +2791,13 @@
     
     private final InputHandler mInputHandler = new InputHandler() {
         public void handleKey(KeyEvent event, Runnable finishedCallback) {
-            if (mFinishedCallback != null) {
-                Slog.w(TAG, "Received a new key event from the input queue but there is "
-                        + "already an unfinished key event in progress.");
-            }
-
-            mFinishedCallback = finishedCallback;
-
+            startInputEvent(finishedCallback);
             dispatchKey(event, true);
         }
 
         public void handleMotion(MotionEvent event, Runnable finishedCallback) {
-            finishedCallback.run();
-            
-            dispatchMotion(event);
+            startInputEvent(finishedCallback);
+            dispatchMotion(event, true);
         }
     };
 
@@ -2825,26 +2830,43 @@
     }
     
     public void dispatchMotion(MotionEvent event) {
+        dispatchMotion(event, false);
+    }
+
+    private void dispatchMotion(MotionEvent event, boolean sendDone) {
         int source = event.getSource();
         if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            dispatchPointer(event);
+            dispatchPointer(event, sendDone);
         } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-            dispatchTrackball(event);
+            dispatchTrackball(event, sendDone);
         } else {
             // TODO
             Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
+            if (sendDone) {
+                finishInputEvent();
+            }
         }
     }
 
     public void dispatchPointer(MotionEvent event) {
+        dispatchPointer(event, false);
+    }
+
+    private void dispatchPointer(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_POINTER);
         msg.obj = event;
+        msg.arg1 = sendDone ? 1 : 0;
         sendMessageAtTime(msg, event.getEventTime());
     }
 
     public void dispatchTrackball(MotionEvent event) {
+        dispatchTrackball(event, false);
+    }
+
+    private void dispatchTrackball(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_TRACKBALL);
         msg.obj = event;
+        msg.arg1 = sendDone ? 1 : 0;
         sendMessageAtTime(msg, event.getEventTime());
     }
     
@@ -2919,19 +2941,6 @@
         return scrollToRectOrFocus(rectangle, immediate);
     }
 
-    /**
-     * @hide
-     */
-    public void childOverlayStateChanged(View child) {
-        final boolean oldState = mHasOverlay;
-        mHasOverlay = child.isOverlayEnabled();
-        // Invalidate the whole thing when we change overlay states just in case
-        // something left chunks of data drawn someplace it shouldn't have.
-        if (mHasOverlay != oldState) {
-            child.invalidate();
-        }
-    }
-
     class TakenSurfaceHolder extends BaseSurfaceHolder {
         @Override
         public boolean onAllowLockCanvas() {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index eebbc93..9c4aefe 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -583,6 +583,19 @@
          * also been set.
          */
         public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
+        
+        /** Window flag: when set the window will accept for touch events
+         * outside of its bounds to be sent to other windows that also
+         * support split touch.  When this flag is not set, the first pointer
+         * that goes down determines the window to which all subsequent touches
+         * go until all pointers go up.  When this flag is set, each pointer
+         * (not necessarily the first) that goes down determines the window
+         * to which all subsequent touches of that pointer will go until that
+         * pointer goes up thereby enabling touches with multiple pointers
+         * to be split across multiple windows.
+         * 
+         * {@hide} */
+        public static final int FLAG_SPLIT_TOUCH = 0x00800000;
 
         /** Window flag: *sigh* The lock screen wants to continue running its
          * animation while it is fading.  A kind-of hack to allow this.  Maybe
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 55d11bb..8ffa158 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -214,9 +214,9 @@
         // background.
 
         // When device is near-vertical (screen approximately facing the horizon)
-        private static final int DEFAULT_TIME_CONSTANT_MS = 50;
+        private static final int DEFAULT_TIME_CONSTANT_MS = 100;
         // When device is partially tilted towards the sky or ground
-        private static final int TILTED_TIME_CONSTANT_MS = 300;
+        private static final int TILTED_TIME_CONSTANT_MS = 500;
         // When device is under external acceleration, i.e. not just gravity.  We heavily distrust
         // such readings.
         private static final int ACCELERATING_TIME_CONSTANT_MS = 2000;
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 3801948..f7c869b 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -428,8 +428,12 @@
                 a = b;
                 b = tmp;
             }
+            // Clip the end points to be within the content bounds.
+            final int length = content.length();
             if (a < 0) a = 0;
-            if (b > content.length()) b = content.length();
+            if (b < 0) b = 0;
+            if (a > length) a = length;
+            if (b > length) b = length;
 
             ensureDefaultComposingSpans();
             if (mDefaultComposingSpans != null) {
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 219a469..a2c80f2 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -785,11 +785,7 @@
      * @return The BrowserFrame object stored in the new WebView.
      */
     private BrowserFrame createWindow(boolean dialog, boolean userGesture) {
-        WebView w = mCallbackProxy.createWindow(dialog, userGesture);
-        if (w != null) {
-            return w.getWebViewCore().getBrowserFrame();
-        }
-        return null;
+        return mCallbackProxy.createWindow(dialog, userGesture);
     }
 
     /**
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 0e0e032..d65c106 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -1098,7 +1098,7 @@
         }
     }
 
-    public WebView createWindow(boolean dialog, boolean userGesture) {
+    public BrowserFrame createWindow(boolean dialog, boolean userGesture) {
         // Do an unsynchronized quick check to avoid posting if no callback has
         // been set.
         if (mWebChromeClient == null) {
@@ -1122,9 +1122,15 @@
 
         WebView w = transport.getWebView();
         if (w != null) {
-            w.getWebViewCore().initializeSubwindow();
+            WebViewCore core = w.getWebViewCore();
+            // If WebView.destroy() has been called, core may be null.  Skip
+            // initialization in that case and return null.
+            if (core != null) {
+                core.initializeSubwindow();
+                return core.getBrowserFrame();
+            }
         }
-        return w;
+        return null;
     }
 
     public void onRequestFocus() {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 456e0d9d..24b1158c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1284,13 +1284,18 @@
         return mDatabase.getHttpAuthUsernamePassword(host, realm);
     }
 
+    private void clearHelpers() {
+        clearTextEntry(false);
+        selectionDone();
+    }
+
     /**
      * Destroy the internal state of the WebView. This method should be called
      * after the WebView has been removed from the view system. No other
      * methods may be called on a WebView after destroy.
      */
     public void destroy() {
-        clearTextEntry(false);
+        clearHelpers();
         if (mWebViewCore != null) {
             // Set the handlers to null before destroying WebViewCore so no
             // more messages will be posted.
@@ -1608,7 +1613,7 @@
         arg.mUrl = url;
         arg.mExtraHeaders = extraHeaders;
         mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
-        clearTextEntry(false);
+        clearHelpers();
     }
 
     /**
@@ -1637,7 +1642,7 @@
             arg.mUrl = url;
             arg.mPostData = postData;
             mWebViewCore.sendMessage(EventHub.POST_URL, arg);
-            clearTextEntry(false);
+            clearHelpers();
         } else {
             loadUrl(url);
         }
@@ -1693,7 +1698,7 @@
         arg.mEncoding = encoding;
         arg.mHistoryUrl = historyUrl;
         mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
-        clearTextEntry(false);
+        clearHelpers();
     }
 
     /**
@@ -1710,7 +1715,7 @@
      * Reload the current url.
      */
     public void reload() {
-        clearTextEntry(false);
+        clearHelpers();
         switchOutDrawHistory();
         mWebViewCore.sendMessage(EventHub.RELOAD);
     }
@@ -1790,7 +1795,7 @@
 
     private void goBackOrForward(int steps, boolean ignoreSnapshot) {
         if (steps != 0) {
-            clearTextEntry(false);
+            clearHelpers();
             mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
                     ignoreSnapshot ? 1 : 0);
         }
@@ -2945,16 +2950,28 @@
                     if (rangeY > 0 || getOverscrollMode() == OVERSCROLL_ALWAYS) {
                         if (y < 0 && oldY >= 0) {
                             mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
+                            if (!mEdgeGlowBottom.isFinished()) {
+                                mEdgeGlowBottom.onRelease();
+                            }
                         } else if (y > rangeY && oldY <= rangeY) {
                             mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
+                            if (!mEdgeGlowTop.isFinished()) {
+                                mEdgeGlowTop.onRelease();
+                            }
                         }
                     }
 
                     if (rangeX > 0) {
                         if (x < 0 && oldX >= 0) {
                             mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
+                            if (!mEdgeGlowRight.isFinished()) {
+                                mEdgeGlowRight.onRelease();
+                            }
                         } else if (x > rangeX && oldX <= rangeX) {
                             mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
+                            if (!mEdgeGlowLeft.isFinished()) {
+                                mEdgeGlowLeft.onRelease();
+                            }
                         }
                     }
                 }
@@ -4421,7 +4438,7 @@
 
     @Override
     protected void onDetachedFromWindow() {
-        clearTextEntry(false);
+        clearHelpers();
         dismissZoomControl();
         if (hasWindowFocus()) setActive(false);
         super.onDetachedFromWindow();
@@ -4999,7 +5016,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
+        if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
             return false;
         }
 
@@ -5543,8 +5560,14 @@
                     final int pulledToX = oldX + deltaX;
                     if (pulledToX < 0) {
                         mEdgeGlowLeft.onPull((float) deltaX / getWidth());
+                        if (!mEdgeGlowRight.isFinished()) {
+                            mEdgeGlowRight.onRelease();
+                        }
                     } else if (pulledToX > rangeX) {
                         mEdgeGlowRight.onPull((float) deltaX / getWidth());
+                        if (!mEdgeGlowLeft.isFinished()) {
+                            mEdgeGlowLeft.onRelease();
+                        }
                     }
                 }
 
@@ -5552,8 +5575,14 @@
                     final int pulledToY = oldY + deltaY;
                     if (pulledToY < 0) {
                         mEdgeGlowTop.onPull((float) deltaY / getHeight());
+                        if (!mEdgeGlowBottom.isFinished()) {
+                            mEdgeGlowBottom.onRelease();
+                        }
                     } else if (pulledToY > rangeY) {
                         mEdgeGlowBottom.onPull((float) deltaY / getHeight());
+                        if (!mEdgeGlowTop.isFinished()) {
+                            mEdgeGlowTop.onRelease();
+                        }
                     }
                 }
             }
@@ -6954,6 +6983,7 @@
                 case UPDATE_TEXT_ENTRY_MSG_ID:
                     // this is sent after finishing resize in WebViewCore. Make
                     // sure the text edit box is still on the  screen.
+                    selectionDone();
                     if (inEditingMode() && nativeCursorIsTextInput()) {
                         mWebTextView.bringIntoView();
                         rebuildWebTextView();
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 176233e..8d58a6d 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -25,13 +25,13 @@
 
 
 /**
- * <p>
- * <code>Button</code> represents a push-button widget. Push-buttons can be
- * pressed, or clicked, by the user to perform an action. A typical use of a
- * push-button in an activity would be the following:
+ * Represents a push-button widget. Push-buttons can be
+ * pressed, or clicked, by the user to perform an action.
+
+ * <p>A typical use of a push-button in an activity would be the following:
  * </p>
  *
- * <pre class="prettyprint">
+ * <pre>
  * public class MyActivity extends Activity {
  *     protected void onCreate(Bundle icicle) {
  *         super.onCreate(icicle);
@@ -45,16 +45,52 @@
  *             }
  *         });
  *     }
- * }
- * </pre>
+ * }</pre>
  *
- * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
- * tutorial</a>.</p>
+ * <p>However, instead of applying an {@link android.view.View.OnClickListener OnClickListener} to
+ * the button in your activity, you can assign a method to your button in the XML layout,
+ * using the {@link android.R.attr#onClick android:onClick} attribute. For example:</p>
+ *
+ * <pre>
+ * &lt;Button
+ *     android:layout_height="wrap_content"
+ *     android:layout_width="wrap_content"
+ *     android:text="@string/self_destruct"
+ *     android:onClick="selfDestruct" /&gt;</pre>
+ *
+ * <p>Now, when a user clicks the button, the Android system calls the activity's {@code
+ * selfDestruct(View)} method. In order for this to work, the method must be public and accept
+ * a {@link android.view.View} as its only parameter. For example:</p>
+ *
+ * <pre>
+ * public void selfDestruct(View view) {
+ *     // Kabloey
+ * }</pre>
+ *
+ * <p>The {@link android.view.View} passed into the method is a reference to the widget
+ * that was clicked.</p>
+ *
+ * <h3>Button style</h3>
+ *
+ * <p>Every Button is styled using the system's default button background, which is often different
+ * from one device to another and from one version of the platform to another. If you're not
+ * satisfied with the default button style and want to customize it to match the design of your
+ * application, then you can replace the button's background image with a <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">state list drawable</a>.
+ * A state list drawable is a drawable resource defined in XML that changes its image based on
+ * the current state of the button. Once you've defined a state list drawable in XML, you can apply
+ * it to your Button with the {@link android.R.attr#background android:background}
+ * attribute. For more information and an example, see <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">State List
+ * Drawable</a>.</p>
+ *
+ * <p>Also see the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a> for an example implementation of a button.</p>
  *
  * <p><strong>XML attributes</strong></p>
- * <p> 
- * See {@link android.R.styleable#Button Button Attributes}, 
- * {@link android.R.styleable#TextView TextView Attributes},  
+ * <p>
+ * See {@link android.R.styleable#Button Button Attributes},
+ * {@link android.R.styleable#TextView TextView Attributes},
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 93222e1..e24f4954 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -36,25 +36,26 @@
     private static final int PULL_TIME = 250;
 
     // Time it will take for a pulled glow to decay to partial strength before release
-    private static final int PULL_DECAY_TIME = 1000;
+    private static final int PULL_DECAY_TIME = 10000;
 
+    private static final float MAX_ALPHA = 1.f;
     private static final float HELD_EDGE_ALPHA = 0.7f;
     private static final float HELD_EDGE_SCALE_Y = 0.5f;
     private static final float HELD_GLOW_ALPHA = 0.5f;
     private static final float HELD_GLOW_SCALE_Y = 0.5f;
 
-    private static final float MAX_GLOW_HEIGHT = 0.33f;
+    private static final float MAX_GLOW_HEIGHT = 3.f;
 
-    private static final float PULL_GLOW_BEGIN = 0.5f;
+    private static final float PULL_GLOW_BEGIN = 1.f;
     private static final float PULL_EDGE_BEGIN = 0.6f;
 
     // Minimum velocity that will be absorbed
-    private static final int MIN_VELOCITY = 750;
-    
+    private static final int MIN_VELOCITY = 100;
+
     private static final float EPSILON = 0.001f;
 
-    private Drawable mEdge;
-    private Drawable mGlow;
+    private final Drawable mEdge;
+    private final Drawable mGlow;
     private int mWidth;
     private int mHeight;
 
@@ -73,9 +74,9 @@
     private float mGlowScaleYFinish;
 
     private long mStartTime;
-    private int mDuration;
+    private float mDuration;
 
-    private Interpolator mInterpolator;
+    private final Interpolator mInterpolator;
 
     private static final int STATE_IDLE = 0;
     private static final int STATE_PULL = 1;
@@ -83,6 +84,17 @@
     private static final int STATE_RECEDE = 3;
     private static final int STATE_PULL_DECAY = 4;
 
+    // How much dragging should effect the height of the edge image.
+    // Number determined by user testing.
+    private static final int PULL_DISTANCE_EDGE_FACTOR = 5;
+
+    // How much dragging should effect the height of the glow image.
+    // Number determined by user testing.
+    private static final int PULL_DISTANCE_GLOW_FACTOR = 10;
+
+    private static final int VELOCITY_EDGE_FACTOR = 8;
+    private static final int VELOCITY_GLOW_FACTOR = 16;
+
     private int mState = STATE_IDLE;
 
     private float mPullDistance;
@@ -109,6 +121,7 @@
 
     /**
      * Call when the object is pulled by the user.
+     *
      * @param deltaDistance Change in distance since the last call
      */
     public void onPull(float deltaDistance) {
@@ -127,11 +140,12 @@
         mPullDistance += deltaDistance;
         float distance = Math.abs(mPullDistance);
 
-        mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, 1.f));
-        mEdgeScaleY = mEdgeScaleYStart = Math.max(HELD_EDGE_SCALE_Y, Math.min(distance, 2.f));
+        mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
+        mEdgeScaleY = mEdgeScaleYStart = Math.max(
+                HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
 
-        mGlowAlpha = mGlowAlphaStart = Math.max(0.5f,
-                Math.min(mGlowAlpha + Math.abs(deltaDistance), 1.f));
+        mGlowAlpha = mGlowAlphaStart = Math.max(
+                0.5f, Math.min(mGlowAlpha + Math.abs(deltaDistance), MAX_ALPHA));
 
         float glowChange = Math.abs(deltaDistance);
         if (deltaDistance > 0 && mPullDistance < 0) {
@@ -140,7 +154,8 @@
         if (mPullDistance == 0) {
             mGlowScaleY = 0;
         }
-        mGlowScaleY = mGlowScaleYStart = Math.max(0, mGlowScaleY + glowChange * 2);
+        mGlowScaleY = mGlowScaleYStart = Math.max(
+                0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR);
 
         mEdgeAlphaFinish = mEdgeAlpha;
         mEdgeScaleYFinish = mEdgeScaleY;
@@ -165,9 +180,9 @@
         mGlowScaleYStart = mGlowScaleY;
 
         mEdgeAlphaFinish = 0.f;
-        mEdgeScaleYFinish = 0.1f;
+        mEdgeScaleYFinish = 0.f;
         mGlowAlphaFinish = 0.f;
-        mGlowScaleYFinish = 0.1f;
+        mGlowScaleYFinish = 0.f;
 
         mStartTime = AnimationUtils.currentAnimationTimeMillis();
         mDuration = RECEDE_TIME;
@@ -175,6 +190,7 @@
 
     /**
      * Call when the effect absorbs an impact at the given velocity.
+     *
      * @param velocity Velocity at impact in pixels per second.
      */
     public void onAbsorb(int velocity) {
@@ -182,27 +198,43 @@
         velocity = Math.max(MIN_VELOCITY, Math.abs(velocity));
 
         mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mDuration = (int) (velocity * 0.03f);
+        mDuration = 0.1f + (velocity * 0.03f);
 
+        // The edge should always be at least partially visible, regardless
+        // of velocity.
         mEdgeAlphaStart = 0.5f;
         mEdgeScaleYStart = 0.2f;
+        // The glow depends more on the velocity, and therefore starts out
+        // nearly invisible.
         mGlowAlphaStart = 0.5f;
         mGlowScaleYStart = 0.f;
 
-        mEdgeAlphaFinish = Math.max(0, Math.min(velocity * 0.01f, 1));
+        // Factor the velocity by 8. Testing on device shows this works best to
+        // reflect the strength of the user's scrolling.
+        mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
+        // Edge should never get larger than the size of its asset.
         mEdgeScaleYFinish = 1.f;
-        mGlowAlphaFinish = 1.f;
-        mGlowScaleYFinish = Math.min(velocity * 0.001f, 1);
+
+        // Growth for the size of the glow should be quadratic to properly
+        // respond
+        // to a user's scrolling speed. The faster the scrolling speed, the more
+        // intense the effect should be for both the size and the saturation.
+        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
+        // Alpha should change for the glow as well as size.
+        mGlowAlphaFinish = Math.max(
+                mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
     }
 
+
     /**
-     * Draw into the provided canvas.
-     * Assumes that the canvas has been rotated accordingly and the size has been set.
-     * The effect will be drawn the full width of X=0 to X=width, emitting from Y=0 and extending
-     * to some factor < 1.f of height.
+     * Draw into the provided canvas. Assumes that the canvas has been rotated
+     * accordingly and the size has been set. The effect will be drawn the full
+     * width of X=0 to X=width, emitting from Y=0 and extending to some factor <
+     * 1.f of height.
      *
      * @param canvas Canvas to draw into
-     * @return true if drawing should continue beyond this frame to continue the animation
+     * @return true if drawing should continue beyond this frame to continue the
+     *         animation
      */
     public boolean draw(Canvas canvas) {
         update();
@@ -213,15 +245,14 @@
         final float distScale = (float) mHeight / mWidth;
 
         mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
-        mGlow.setBounds(0, 0, mWidth, (int) Math.min(glowHeight * mGlowScaleY * distScale * 0.6f,
-                mHeight * MAX_GLOW_HEIGHT));
+        // Width of the image should be 3 * the width of the screen.
+        // Should start off screen to the left.
+        mGlow.setBounds(-mWidth, 0, mWidth * 2, (int) Math.min(
+                glowHeight * mGlowScaleY * distScale * 0.6f, mHeight * MAX_GLOW_HEIGHT));
         mGlow.draw(canvas);
 
         mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
-        mEdge.setBounds(0,
-                0,
-                mWidth,
-                (int) (edgeHeight * mEdgeScaleY));
+        mEdge.setBounds(0, 0, mWidth, (int) (edgeHeight * mEdgeScaleY));
         mEdge.draw(canvas);
 
         return mState != STATE_IDLE;
@@ -229,7 +260,7 @@
 
     private void update() {
         final long time = AnimationUtils.currentAnimationTimeMillis();
-        final float t = Math.min((float) (time - mStartTime) / mDuration, 1.f);
+        final float t = Math.min((time - mStartTime) / mDuration, 1.f);
 
         final float interp = mInterpolator.getInterpolation(t);
 
@@ -251,7 +282,7 @@
                     mGlowScaleYStart = mGlowScaleY;
 
                     mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = 0.1f;
+                    mEdgeScaleYFinish = mEdgeScaleY;
                     mGlowAlphaFinish = 0.f;
                     mGlowScaleYFinish = mGlowScaleY;
                     break;
@@ -265,10 +296,11 @@
                     mGlowAlphaStart = mGlowAlpha;
                     mGlowScaleYStart = mGlowScaleY;
 
-                    mEdgeAlphaFinish = Math.min(mEdgeAlphaStart, HELD_EDGE_ALPHA);
-                    mEdgeScaleYFinish = Math.min(mEdgeScaleYStart, HELD_EDGE_SCALE_Y);
-                    mGlowAlphaFinish = Math.min(mGlowAlphaStart, HELD_GLOW_ALPHA);
-                    mGlowScaleYFinish = Math.min(mGlowScaleY, HELD_GLOW_SCALE_Y);
+                    // After a pull, the glow should fade to nothing.
+                    mEdgeAlphaFinish = 0.f;
+                    mEdgeScaleYFinish = 0.f;
+                    mGlowAlphaFinish = 0.f;
+                    mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL_DECAY:
                     // Do nothing; wait for release
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0378328..7fe6190 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -85,6 +85,7 @@
     private boolean mTouchable = true;
     private boolean mOutsideTouchable = false;
     private boolean mClippingEnabled = true;
+    private boolean mSplitTouchEnabled;
 
     private OnTouchListener mTouchInterceptor;
     
@@ -165,6 +166,7 @@
                 attrs, com.android.internal.R.styleable.PopupWindow, defStyle, 0);
 
         mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
+        mAnimationStyle = a.getResourceId(R.styleable.PopupWindow_windowAnimationStyle, -1);
 
         // If this is a StateListDrawable, try to find and store the drawable to be
         // used when the drop-down is placed above its anchor view, and the one to be
@@ -564,6 +566,36 @@
     }
 
     /**
+     * <p>Indicates whether the popup window supports splitting touches.</p>
+     * 
+     * @return true if the touch splitting is enabled, false otherwise
+     * 
+     * @see #setSplitTouchEnabled(boolean)
+     * @hide
+     */
+    public boolean isSplitTouchEnabled() {
+        return mSplitTouchEnabled;
+    }
+
+    /**
+     * <p>Allows the popup window to split touches across other windows that also
+     * support split touch.  When this flag is not set, the first pointer
+     * that goes down determines the window to which all subsequent touches
+     * go until all pointers go up.  When this flag is set, each pointer
+     * (not necessarily the first) that goes down determines the window
+     * to which all subsequent touches of that pointer will go until that
+     * pointer goes up thereby enabling touches with multiple pointers
+     * to be split across multiple windows.</p>
+     *
+     * @param enabled true if the split touches should be enabled, false otherwise
+     * @see #isSplitTouchEnabled()
+     * @hide
+     */
+    public void setSplitTouchEnabled(boolean enabled) {
+        mSplitTouchEnabled = enabled;
+    }
+
+    /**
      * <p>Change the width and height measure specs that are given to the
      * window manager by the popup.  By default these are 0, meaning that
      * the current width or height is requested as an explicit size from
@@ -888,6 +920,9 @@
         if (!mClippingEnabled) {
             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
         }
+        if (mSplitTouchEnabled) {
+            curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+        }
         return curFlags;
     }
     
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a09b0c8..6278192 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -194,6 +194,8 @@
     
     private static int PRIORITY = 100;
 
+    private final int[] mTempCoords = new int[2];
+
     private ColorStateList mTextColor;
     private int mCurTextColor;
     private ColorStateList mHintTextColor;
@@ -3749,8 +3751,6 @@
             showError();
             mShowErrorAfterAttach = false;
         }
-        
-        updateOverlay();
     }
 
     @Override
@@ -3768,8 +3768,6 @@
         if (mError != null) {
             hideError();
         }
-        
-        setOverlayEnabled(false);
     }
 
     @Override
@@ -4122,19 +4120,6 @@
         canvas.restore();
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    public void onDrawOverlay(Canvas canvas) {
-        if (mInsertionPointCursorController != null) {
-            mInsertionPointCursorController.draw(canvas);
-        }
-        if (mSelectionModifierCursorController != null) {
-            mSelectionModifierCursorController.draw(canvas);
-        }
-    }
-
     @Override
     public void getFocusedRect(Rect r) {
         if (mLayout == null) {
@@ -6705,31 +6690,17 @@
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
-            // Check to see if we're testing for our anchor overlay.
-            boolean handled = false;
-            final float x = event.getX();
-            final float y = event.getY();
-            if (x < 0 || x >= mRight - mLeft || y < 0 || y >= mBottom - mTop) {
-                if (mInsertionPointCursorController != null) {
-                    handled |= mInsertionPointCursorController.onTouchEvent(event);
-                }
-                if (mSelectionModifierCursorController != null) {
-                    handled |= mSelectionModifierCursorController.onTouchEvent(event);
-                }
-
-                if (!handled) {
-                    return false;
-                }
+            if (mInsertionPointCursorController != null) {
+                mInsertionPointCursorController.onTouchEvent(event);
+            }
+            if (mSelectionModifierCursorController != null) {
+                mSelectionModifierCursorController.onTouchEvent(event);
             }
 
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
             mTouchFocusSelected = false;
             mScrolled = false;
-
-            if (handled) {
-                return true;
-            }
         }
 
         final boolean superResult = super.onTouchEvent(event);
@@ -6745,7 +6716,6 @@
         }
 
         if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {
-
             if (mInsertionPointCursorController != null) {
                 mInsertionPointCursorController.onTouchEvent(event);
             }
@@ -6758,12 +6728,23 @@
             // Save previous selection, in case this event is used to show the IME.
             int oldSelStart = getSelectionStart();
             int oldSelEnd = getSelectionEnd();
+
+            final int oldScrollX = mScrollX;
+            final int oldScrollY = mScrollY;
             
             if (mMovement != null) {
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
 
             if (isTextEditable()) {
+                if (mScrollX != oldScrollX || mScrollY != oldScrollY) {
+                    // Hide insertion anchor while scrolling. Leave selection.
+                    hideInsertionPointCursorController();
+                    if (mSelectionModifierCursorController != null &&
+                            mSelectionModifierCursorController.isShowing()) {
+                        mSelectionModifierCursorController.updatePosition();
+                    }
+                }
                 if (action == MotionEvent.ACTION_UP && isFocused() && !mScrolled) {
                     InputMethodManager imm = (InputMethodManager)
                           getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -7618,27 +7599,12 @@
         }
     }
 
-    private void updateOverlay() {
-        boolean enableOverlay = false;
-        if (mSelectionModifierCursorController != null) {
-            enableOverlay |= mSelectionModifierCursorController.isShowing();
-        }
-        if (mInsertionPointCursorController != null) {
-            enableOverlay |= mInsertionPointCursorController.isShowing();
-        }
-        setOverlayEnabled(enableOverlay);
-    }
-
     /**
      * A CursorController instance can be used to control a cursor in the text.
-     *
-     * It can be passed to an {@link ArrowKeyMovementMethod} which can intercepts events
-     * and send them to this object instead of the cursor.
+     * It is not used outside of {@link TextView}.
+     * @hide
      */
-    public interface CursorController {
-        /* Cursor fade-out animation duration, in milliseconds. */
-        static final int FADE_OUT_DURATION = 400;
-
+    private interface CursorController {
         /**
          * Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
          * See also {@link #hide()}.
@@ -7659,19 +7625,9 @@
         /**
          * Update the controller's position.
          */
-        public void updatePosition(int x, int y);
+        public void updatePosition(HandleView handle, int x, int y);
 
-        /**
-         * The controller and the cursor's positions can be link by a fixed offset,
-         * computed when the controller is touched, and then maintained as it moves
-         * @return Horizontal offset between the controller and the cursor.
-         */
-        public float getOffsetX();
-
-        /**
-         * @return Vertical offset between the controller and the cursor.
-         */
-        public float getOffsetY();
+        public void updatePosition();
 
         /**
          * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
@@ -7679,330 +7635,267 @@
          * @param event The touch event
          */
         public boolean onTouchEvent(MotionEvent event);
-
-        /**
-         * Draws a visual representation of the controller on the canvas.
-         *
-         * Called at the end of {@link #draw(Canvas)}, in the content coordinates system.
-         * @param canvas The Canvas used by this TextView.
-         */
-        public void draw(Canvas canvas);
     }
 
-    private class Handle {
-        Drawable mDrawable;
-        // Vertical extension of the touch region
-        int mTopExtension, mBottomExtension;
-        // Position of the virtual finger position on screen
-        int mHotSpotVerticalPosition;
+    private class HandleView extends View {
+        private boolean mPositionOnTop = false;
+        private Drawable mDrawable;
+        private PopupWindow mContainer;
+        private int mPositionX;
+        private int mPositionY;
+        private CursorController mController;
+        private boolean mIsDragging;
 
-        Handle(Drawable drawable) {
-            mDrawable = drawable;
+        public HandleView(CursorController controller, Drawable handle) {
+            super(TextView.this.mContext);
+            mController = controller;
+            mDrawable = handle;
+            mContainer = new PopupWindow(TextView.this.mContext, null,
+                    com.android.internal.R.attr.textSelectHandleWindowStyle);
+            mContainer.setSplitTouchEnabled(true);
+        }
+
+        @Override
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            setMeasuredDimension(mDrawable.getIntrinsicWidth(),
+                    mDrawable.getIntrinsicHeight());
+        }
+
+        public void show() {
+            if (!isPositionInBounds()) {
+                hide();
+                return;
+            }
+            mContainer.setContentView(this);
+            final int[] coords = mTempCoords;
+            TextView.this.getLocationOnScreen(coords);
+            coords[0] += mPositionX;
+            coords[1] += mPositionY;
+            mContainer.showAtLocation(TextView.this, 0, coords[0], coords[1]);
+        }
+
+        public void hide() {
+            mIsDragging = false;
+            mContainer.dismiss();
+        }
+
+        public boolean isShowing() {
+            return mContainer.isShowing();
+        }
+
+        private boolean isPositionInBounds() {
+            final int extendedPaddingTop = getExtendedPaddingTop();
+            final int extendedPaddingBottom = getExtendedPaddingBottom();
+            final int compoundPaddingLeft = getCompoundPaddingLeft();
+            final int compoundPaddingRight = getCompoundPaddingRight();
+
+            final TextView hostView = TextView.this;
+            final int right = hostView.mRight;
+            final int left = hostView.mLeft;
+            final int bottom = hostView.mBottom;
+            final int top = hostView.mTop;
+
+            final int clipLeft = left + compoundPaddingLeft;
+            final int clipTop = top + extendedPaddingTop;
+            final int clipRight = right - compoundPaddingRight;
+            final int clipBottom = bottom - extendedPaddingBottom;
+
+            final int handleWidth = mDrawable.getIntrinsicWidth();
+            return mPositionX >= clipLeft - handleWidth * 0.75f &&
+                    mPositionX <= clipRight + handleWidth * 0.25f &&
+                    mPositionY >= clipTop && mPositionY <= clipBottom;
+        }
+
+        private void moveTo(int x, int y) {
+            mPositionX = x - TextView.this.mScrollX;
+            mPositionY = y - TextView.this.mScrollY;
+            if (isPositionInBounds()) {
+                if (mContainer.isShowing()){
+                    final int[] coords = mTempCoords;
+                    TextView.this.getLocationOnScreen(coords);
+                    coords[0] += mPositionX;
+                    coords[1] += mPositionY;
+                    mContainer.update(coords[0], coords[1], mRight - mLeft, mBottom - mTop);
+                } else {
+                    show();
+                }
+            } else {
+                hide();
+            }
+        }
+
+        @Override
+        public void onDraw(Canvas c) {
+            mDrawable.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
+            if (mPositionOnTop) {
+                c.save();
+                c.rotate(180, (mRight - mLeft) / 2, (mBottom - mTop) / 2);
+                mDrawable.draw(c);
+                c.restore();
+            } else {
+                mDrawable.draw(c);
+            }
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mIsDragging = true;
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float rawX = ev.getRawX();
+                final float rawY = ev.getRawY();
+                final int[] coords = mTempCoords;
+                TextView.this.getLocationOnScreen(coords);
+                final int x = (int) (rawX - coords[0] + 0.5f);
+                final int y = (int) (rawY - coords[1] + 0.5f);
+                mController.updatePosition(this, x, y);
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mIsDragging = false;
+            }
+            return true;
+        }
+
+        public boolean isDragging() {
+            return mIsDragging;
         }
 
         void positionAtCursor(final int offset, boolean bottom) {
-            final int drawableWidth = mDrawable.getIntrinsicWidth();
-            final int drawableHeight = mDrawable.getIntrinsicHeight();
+            final int width = mDrawable.getIntrinsicWidth();
+            final int height = mDrawable.getIntrinsicHeight();
             final int line = mLayout.getLineForOffset(offset);
             final int lineTop = mLayout.getLineTop(line);
             final int lineBottom = mLayout.getLineBottom(line);
 
-            mHotSpotVerticalPosition = lineTop;
-
             final Rect bounds = sCursorControllerTempRect;
-            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0)
-                + mScrollX;
-            bounds.top = (bottom ? lineBottom : lineTop) + mScrollY;
+            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - width / 2.0)
+                + TextView.this.mScrollX;
+            bounds.top = (bottom ? lineBottom : lineTop) + TextView.this.mScrollY;
 
-            mTopExtension = bottom ? 0 : drawableHeight / 2;
-            mBottomExtension = 0; //drawableHeight / 4;
-
-            // Extend touch region up when editing the last line of text (or a single line) so that
-            // it is easier to grab.
-            if (line == mLayout.getLineCount() - 1) {
-                mTopExtension = (lineBottom - lineTop) - drawableHeight / 2;
-            }
-
-            bounds.right = bounds.left + drawableWidth;
-            bounds.bottom = bounds.top + drawableHeight;
+            bounds.right = bounds.left + width;
+            bounds.bottom = bounds.top + height;
 
             convertFromViewportToContentCoordinates(bounds);
-            invalidate();
-            mDrawable.setBounds(bounds);
-            invalidate();
-        }
-
-        boolean hasFingerOn(float x, float y) {
-            // Simulate a 'fat finger' to ease grabbing of the controller.
-            // Expands according to controller image size instead of using dip distance.
-            // Assumes controller imager has a sensible size, proportionnal to screen density.
-            final int drawableWidth = mDrawable.getIntrinsicWidth();
-            final Rect fingerRect = sCursorControllerTempRect;
-            fingerRect.set((int) (x - drawableWidth / 2.0),
-                           (int) (y - mBottomExtension),
-                           (int) (x + drawableWidth / 2.0),
-                           (int) (y + mTopExtension));
-            fingerRect.offset(mScrollX, mScrollY);
-            return Rect.intersects(mDrawable.getBounds(), fingerRect);
-        }
-
-        void invalidate() {
-            final Rect bounds = mDrawable.getBounds();
-            TextView.this.invalidate(bounds.left, bounds.top,
-                    bounds.right, bounds.bottom);
-        }
-
-        void postInvalidate() {
-            final Rect bounds = mDrawable.getBounds();
-            TextView.this.postInvalidate(bounds.left, bounds.top,
-                    bounds.right, bounds.bottom);
-        }
-
-        void postInvalidateDelayed(long delay) {
-            final Rect bounds = mDrawable.getBounds();
-            TextView.this.postInvalidateDelayed(delay, bounds.left, bounds.top,
-                                                       bounds.right, bounds.bottom);
+            moveTo(bounds.left, bounds.top);
         }
     }
 
-    class InsertionPointCursorController implements CursorController {
-        private static final int DELAY_BEFORE_FADE_OUT = 2100;
+    private class InsertionPointCursorController implements CursorController {
+        private static final int DELAY_BEFORE_FADE_OUT = 4100;
 
-        // Whether or not the cursor control is currently visible
-        private boolean mIsVisible = false;
-        // Starting time of the fade timer
-        private long mFadeOutTimerStart;
         // The cursor controller image
-        private final Handle mHandle;
-        // Used to detect a tap (vs drag) on the controller
-        private long mOnDownTimerStart;
-        // Offset between finger hot point on cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
+        private final HandleView mHandle;
+
+        private final Runnable mHider = new Runnable() {
+            public void run() {
+                hide();
+            }
+        };
 
         InsertionPointCursorController() {
             Resources res = mContext.getResources();
-            mHandle = new Handle(res.getDrawable(mTextSelectHandleRes));
+            mHandle = new HandleView(this, res.getDrawable(mTextSelectHandleRes));
         }
 
         public void show() {
-            updateDrawablePosition();
-            // Has to be done after updateDrawablePosition, so that previous position invalidate
-            // in only done if necessary.
-            mIsVisible = true;
-            updateOverlay();
+            updatePosition();
+            mHandle.show();
+            hideDelayed(DELAY_BEFORE_FADE_OUT);
         }
 
         public void hide() {
-            if (mIsVisible) {
-                long time = System.currentTimeMillis();
-                // Start fading out, only if not already in progress
-                if (time - mFadeOutTimerStart < DELAY_BEFORE_FADE_OUT) {
-                    mFadeOutTimerStart = time - DELAY_BEFORE_FADE_OUT;
-                    mHandle.postInvalidate();
-                }
-            }
+            mHandle.hide();
+            TextView.this.removeCallbacks(mHider);
+        }
+
+        private void hideDelayed(int msec) {
+            TextView.this.removeCallbacks(mHider);
+            TextView.this.postDelayed(mHider, msec);
         }
 
         public boolean isShowing() {
-            return mIsVisible;
+            return mHandle.isShowing();
         }
 
-        public void draw(Canvas canvas) {
-            if (mIsVisible) {
-                int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
-                if (time <= DELAY_BEFORE_FADE_OUT) {
-                    mHandle.postInvalidateDelayed(DELAY_BEFORE_FADE_OUT - time);
-                } else {
-                    time -= DELAY_BEFORE_FADE_OUT;
-                    if (time <= FADE_OUT_DURATION) {
-                        final int alpha = (int)
-                                         ((255.0 * (FADE_OUT_DURATION - time)) / FADE_OUT_DURATION);
-                        mHandle.mDrawable.setAlpha(alpha);
-                        mHandle.postInvalidateDelayed(30);
-                    } else {
-                        mHandle.mDrawable.setAlpha(0);
-                        mIsVisible = false;
-                        updateOverlay();
-                    }
-                }
-                mHandle.mDrawable.draw(canvas);
-            }
-        }
-
-        public void updatePosition(int x, int y) {
+        public void updatePosition(HandleView handle, int x, int y) {
             final int previousOffset = getSelectionStart();
             int offset = getHysteresisOffset(x, y, previousOffset);
 
             if (offset != previousOffset) {
                 Selection.setSelection((Spannable) mText, offset);
-                updateDrawablePosition();
+                updatePosition();
             }
+            hideDelayed(DELAY_BEFORE_FADE_OUT);
         }
 
-        private void updateDrawablePosition() {
-            if (mIsVisible) {
-                // Clear previous cursor controller before bounds are updated
-                mHandle.postInvalidate();
-            }
-
+        public void updatePosition() {
             final int offset = getSelectionStart();
 
             if (offset < 0) {
                 // Should never happen, safety check.
                 Log.w(LOG_TAG, "Update cursor controller position called with no cursor");
-                mIsVisible = false;
-                updateOverlay();
+                hide();
                 return;
             }
 
             mHandle.positionAtCursor(offset, true);
-
-            mFadeOutTimerStart = System.currentTimeMillis();
-            mHandle.mDrawable.setAlpha(255);
         }
 
-        public boolean onTouchEvent(MotionEvent event) {
-            if (isFocused() && isTextEditable() && mIsVisible) {
-                switch (event.getActionMasked()) {
-                    case MotionEvent.ACTION_DOWN : {
-                        final float x = event.getX();
-                        final float y = event.getY();
-
-                        if (mHandle.hasFingerOn(x, y)) {
-                            show();
-
-                            if (mMovement instanceof ArrowKeyMovementMethod) {
-                                ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
-                            }
-
-                            if (mParent != null) {
-                                // Prevent possible scrollView parent from scrolling, so that
-                                // we can use auto-scrolling.
-                                mParent.requestDisallowInterceptTouchEvent(true);
-                            }
-
-                            final Rect bounds = mHandle.mDrawable.getBounds();
-                            mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
-                            mOffsetY = mHandle.mHotSpotVerticalPosition - y;
-
-                            mOffsetX += viewportToContentHorizontalOffset();
-                            mOffsetY += viewportToContentVerticalOffset();
-
-                            mOnDownTimerStart = event.getEventTime();
-                            return true;
-                        }
-                        return false;
-                    }
-
-                    case MotionEvent.ACTION_UP : {
-                        int time = (int) (event.getEventTime() - mOnDownTimerStart);
-
-                        if (time <= ViewConfiguration.getTapTimeout()) {
-                            // A tap on the controller (not a drag) will move the cursor
-                            int offset = getOffset((int) event.getX(), (int) event.getY());
-                            Selection.setSelection((Spannable) mText, offset);
-
-                            // Modified by cancelLongPress and prevents the cursor from changing
-                            mScrolled = false;
-                        }
-                        break;
-                    }
-                }
-            }
+        public boolean onTouchEvent(MotionEvent ev) {
             return false;
         }
-
-        public float getOffsetX() {
-            return mOffsetX;
-        }
-
-        public float getOffsetY() {
-            return mOffsetY;
-        }
     }
 
-    class SelectionModifierCursorController implements CursorController {
-        // Whether or not the selection controls are currently visible
-        private boolean mIsVisible = false;
-        // Whether that start or the end of selection controller is dragged
-        private boolean mStartIsDragged = false;
-        // Starting time of the fade timer
-        private long mFadeOutTimerStart;
-        // Used to detect a tap (vs drag) on the controller
-        private long mOnDownTimerStart;
+    private class SelectionModifierCursorController implements CursorController {
         // The cursor controller images
-        private final Handle mStartHandle, mEndHandle;
-        // Offset between finger hot point on active cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
+        private HandleView mStartHandle, mEndHandle;
         // The offsets of that last touch down event. Remembered to start selection there.
         private int mMinTouchOffset, mMaxTouchOffset;
+        // Whether selection anchors are active
+        private boolean mIsShowing;
 
         SelectionModifierCursorController() {
             Resources res = mContext.getResources();
-            mStartHandle = new Handle(res.getDrawable(mTextSelectHandleLeftRes));
-            mEndHandle = new Handle(res.getDrawable(mTextSelectHandleRightRes));
+            mStartHandle = new HandleView(this, res.getDrawable(mTextSelectHandleLeftRes));
+            mEndHandle = new HandleView(this, res.getDrawable(mTextSelectHandleRightRes));
         }
 
         public void show() {
-            updateDrawablesPositions();
-            // Has to be done after updateDrawablePositions, so that previous position invalidate
-            // in only done if necessary.
-            mIsVisible = true;
-            updateOverlay();
-            mFadeOutTimerStart = -1;
+            mIsShowing = true;
+            updatePosition();
+            mStartHandle.show();
+            mEndHandle.show();
             hideInsertionPointCursorController();
         }
 
         public void hide() {
-            if (mIsVisible && (mFadeOutTimerStart < 0)) {
-                mFadeOutTimerStart = System.currentTimeMillis();
-                mStartHandle.postInvalidate();
-                mEndHandle.postInvalidate();
-            }
+            mStartHandle.hide();
+            mEndHandle.hide();
+            mIsShowing = false;
         }
 
         public boolean isShowing() {
-            return mIsVisible;
+            return mIsShowing;
         }
 
         public void cancelFadeOutAnimation() {
-            mIsVisible = false;
-            updateOverlay();
-            mStartHandle.postInvalidate();
-            mEndHandle.postInvalidate();
+            hide();
         }
 
-        public void draw(Canvas canvas) {
-            if (mIsVisible) {
-                if (mFadeOutTimerStart >= 0) {
-                    int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
-                    if (time <= FADE_OUT_DURATION) {
-                        final int alpha = 255 * (FADE_OUT_DURATION - time) / FADE_OUT_DURATION;
-                        mStartHandle.mDrawable.setAlpha(alpha);
-                        mEndHandle.mDrawable.setAlpha(alpha);
-                        mStartHandle.postInvalidateDelayed(30);
-                        mEndHandle.postInvalidateDelayed(30);
-                    } else {
-                        mStartHandle.mDrawable.setAlpha(0);
-                        mEndHandle.mDrawable.setAlpha(0);
-                        mIsVisible = false;
-                        updateOverlay();
-                    }
-                }
-                mStartHandle.mDrawable.draw(canvas);
-                mEndHandle.mDrawable.draw(canvas);
-            }
-        }
-
-        public void updatePosition(int x, int y) {
+        public void updatePosition(HandleView handle, int x, int y) {
             int selectionStart = getSelectionStart();
             int selectionEnd = getSelectionEnd();
 
-            final int previousOffset = mStartIsDragged ? selectionStart : selectionEnd;
+            final int previousOffset = handle == mStartHandle ? selectionStart : selectionEnd;
             int offset = getHysteresisOffset(x, y, previousOffset);
 
             // Handle the case where start and end are swapped, making sure start <= end
-            if (mStartIsDragged) {
+            if (handle == mStartHandle) {
                 if (offset <= selectionEnd) {
                     if (selectionStart == offset) {
                         return; // no change, no need to redraw;
@@ -8011,7 +7904,9 @@
                 } else {
                     selectionStart = selectionEnd;
                     selectionEnd = offset;
-                    mStartIsDragged = false;
+                    HandleView temp = mStartHandle;
+                    mStartHandle = mEndHandle;
+                    mEndHandle = temp;
                 }
             } else {
                 if (offset >= selectionStart) {
@@ -8022,43 +7917,35 @@
                 } else {
                     selectionEnd = selectionStart;
                     selectionStart = offset;
-                    mStartIsDragged = true;
+                    HandleView temp = mStartHandle;
+                    mStartHandle = mEndHandle;
+                    mEndHandle = temp;
                 }
             }
 
             Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
-            updateDrawablesPositions();
+            updatePosition();
         }
 
-        private void updateDrawablesPositions() {
-            if (mIsVisible) {
-                // Clear previous cursor controller before bounds are updated
-                mStartHandle.postInvalidate();
-                mEndHandle.postInvalidate();
-            }
-
+        public void updatePosition() {
             final int selectionStart = getSelectionStart();
             final int selectionEnd = getSelectionEnd();
 
             if ((selectionStart < 0) || (selectionEnd < 0)) {
                 // Should never happen, safety check.
                 Log.w(LOG_TAG, "Update selection controller position called with no cursor");
-                mIsVisible = false;
-                updateOverlay();
+                hide();
                 return;
             }
 
             boolean oneLineSelection = mLayout.getLineForOffset(selectionStart) ==
-                mLayout.getLineForOffset(selectionEnd);
+                    mLayout.getLineForOffset(selectionEnd);
             mStartHandle.positionAtCursor(selectionStart, oneLineSelection);
             mEndHandle.positionAtCursor(selectionEnd, true);
-
-            mStartHandle.mDrawable.setAlpha(255);
-            mEndHandle.mDrawable.setAlpha(255);
         }
 
         public boolean onTouchEvent(MotionEvent event) {
-            if (isTextEditable()) {
+            if (isFocused() && isTextEditable()) {
                 switch (event.getActionMasked()) {
                     case MotionEvent.ACTION_DOWN:
                         final int x = (int) event.getX();
@@ -8067,44 +7954,6 @@
                         // Remember finger down position, to be able to start selection from there
                         mMinTouchOffset = mMaxTouchOffset = mLastTouchOffset = getOffset(x, y);
 
-                        if (mIsVisible) {
-                            if (mMovement instanceof ArrowKeyMovementMethod) {
-                                boolean isOnStart = mStartHandle.hasFingerOn(x, y);
-                                boolean isOnEnd = mEndHandle.hasFingerOn(x, y);
-                                if (isOnStart || isOnEnd) {
-                                    if (mParent != null) {
-                                        // Prevent possible scrollView parent from scrolling, so
-                                        // that we can use auto-scrolling.
-                                        mParent.requestDisallowInterceptTouchEvent(true);
-                                    }
-
-                                    // In case both controllers are under finger (very small
-                                    // selection region), arbitrarily pick end controller.
-                                    mStartIsDragged = !isOnEnd;
-                                    final Handle draggedHandle =
-                                        mStartIsDragged ? mStartHandle : mEndHandle;
-                                    final Rect bounds = draggedHandle.mDrawable.getBounds();
-                                    mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
-                                    mOffsetY = draggedHandle.mHotSpotVerticalPosition - y;
-
-                                    mOffsetX += viewportToContentHorizontalOffset();
-                                    mOffsetY += viewportToContentVerticalOffset();
-
-                                    mOnDownTimerStart = event.getEventTime();
-                                    ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
-                                    return true;
-                                }
-                            }
-                        }
-                        break;
-
-                    case MotionEvent.ACTION_UP:
-                        int time = (int) (event.getEventTime() - mOnDownTimerStart);
-
-                        if (time <= ViewConfiguration.getTapTimeout()) {
-                            // A tap on the controller (not a drag) opens the contextual Copy menu
-                            showContextMenu();
-                        }
                         break;
 
                     case MotionEvent.ACTION_POINTER_DOWN:
@@ -8143,19 +7992,11 @@
             return mMaxTouchOffset;
         }
 
-        public float getOffsetX() {
-            return mOffsetX;
-        }
-
-        public float getOffsetY() {
-            return mOffsetY;
-        }
-
         /**
          * @return true iff this controller is currently used to move the selection start.
          */
         public boolean isSelectionStartDragged() {
-            return mIsVisible && mStartIsDragged;
+            return mStartHandle.isDragging();
         }
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c2d003e..b033fad 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -51,6 +51,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * All information we are collecting about things that can happen that impact
@@ -66,7 +67,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 51;
+    private static final int VERSION = 52;
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -84,7 +85,7 @@
 
     static final int MSG_UPDATE_WAKELOCKS = 1;
     static final int MSG_REPORT_POWER_CHANGE = 2;
-    static final long DELAY_UPDATE_WAKELOCKS = 15*1000;
+    static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
 
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
@@ -1473,6 +1474,13 @@
         }
     }
 
+    public void reportExcessiveCpuLocked(int uid, String proc, long overTime, long usedTime) {
+        Uid u = mUidStats.get(uid);
+        if (u != null) {
+            u.reportExcessiveCpuLocked(proc, overTime, usedTime);
+        }
+    }
+
     int mSensorNesting;
 
     public void noteStartSensorLocked(int uid, int sensor) {
@@ -2974,7 +2982,7 @@
 
             SamplingCounter[] mSpeedBins;
 
-            ArrayList<ExcessiveWake> mExcessiveWake;
+            ArrayList<ExcessivePower> mExcessivePower;
 
             Proc() {
                 mUnpluggables.add(this);
@@ -3002,55 +3010,69 @@
                 }
             }
             
-            public int countExcessiveWakes() {
-                return mExcessiveWake != null ? mExcessiveWake.size() : 0;
+            public int countExcessivePowers() {
+                return mExcessivePower != null ? mExcessivePower.size() : 0;
             }
 
-            public ExcessiveWake getExcessiveWake(int i) {
-                if (mExcessiveWake != null) {
-                    return mExcessiveWake.get(i);
+            public ExcessivePower getExcessivePower(int i) {
+                if (mExcessivePower != null) {
+                    return mExcessivePower.get(i);
                 }
                 return null;
             }
 
             public void addExcessiveWake(long overTime, long usedTime) {
-                if (mExcessiveWake == null) {
-                    mExcessiveWake = new ArrayList<ExcessiveWake>();
+                if (mExcessivePower == null) {
+                    mExcessivePower = new ArrayList<ExcessivePower>();
                 }
-                ExcessiveWake ew = new ExcessiveWake();
+                ExcessivePower ew = new ExcessivePower();
+                ew.type = ExcessivePower.TYPE_WAKE;
                 ew.overTime = overTime;
                 ew.usedTime = usedTime;
-                mExcessiveWake.add(ew);
+                mExcessivePower.add(ew);
             }
 
-            void writeExcessiveWakeToParcelLocked(Parcel out) {
-                if (mExcessiveWake == null) {
+            public void addExcessiveCpu(long overTime, long usedTime) {
+                if (mExcessivePower == null) {
+                    mExcessivePower = new ArrayList<ExcessivePower>();
+                }
+                ExcessivePower ew = new ExcessivePower();
+                ew.type = ExcessivePower.TYPE_CPU;
+                ew.overTime = overTime;
+                ew.usedTime = usedTime;
+                mExcessivePower.add(ew);
+            }
+
+            void writeExcessivePowerToParcelLocked(Parcel out) {
+                if (mExcessivePower == null) {
                     out.writeInt(0);
                     return;
                 }
 
-                final int N = mExcessiveWake.size();
+                final int N = mExcessivePower.size();
                 out.writeInt(N);
                 for (int i=0; i<N; i++) {
-                    ExcessiveWake ew = mExcessiveWake.get(i);
+                    ExcessivePower ew = mExcessivePower.get(i);
+                    out.writeInt(ew.type);
                     out.writeLong(ew.overTime);
                     out.writeLong(ew.usedTime);
                 }
             }
 
-            void readExcessiveWakeFromParcelLocked(Parcel in) {
+            void readExcessivePowerFromParcelLocked(Parcel in) {
                 final int N = in.readInt();
                 if (N == 0) {
-                    mExcessiveWake = null;
+                    mExcessivePower = null;
                     return;
                 }
 
-                mExcessiveWake = new ArrayList<ExcessiveWake>();
+                mExcessivePower = new ArrayList<ExcessivePower>();
                 for (int i=0; i<N; i++) {
-                    ExcessiveWake ew = new ExcessiveWake();
+                    ExcessivePower ew = new ExcessivePower();
+                    ew.type = in.readInt();
                     ew.overTime = in.readLong();
                     ew.usedTime = in.readLong();
-                    mExcessiveWake.add(ew);
+                    mExcessivePower.add(ew);
                 }
             }
 
@@ -3079,7 +3101,7 @@
                     }
                 }
 
-                writeExcessiveWakeToParcelLocked(out);
+                writeExcessivePowerToParcelLocked(out);
             }
 
             void readFromParcelLocked(Parcel in) {
@@ -3109,7 +3131,7 @@
                     }
                 }
 
-                readExcessiveWakeFromParcelLocked(in);
+                readExcessivePowerFromParcelLocked(in);
             }
 
             public BatteryStatsImpl getBatteryStats() {
@@ -3743,6 +3765,13 @@
             }
         }
         
+        public void reportExcessiveCpuLocked(String proc, long overTime, long usedTime) {
+            Proc p = getProcessStatsLocked(proc);
+            if (p != null) {
+                p.addExcessiveCpu(overTime, usedTime);
+            }
+        }
+
         public void noteStartSensor(int sensor) {
             StopwatchTimer t = getSensorTimerLocked(sensor, true);
             if (t != null) {
@@ -3834,6 +3863,22 @@
         }
     }
 
+    private HistoryItem mHistoryIterator;
+
+    public boolean startIteratingHistoryLocked() {
+        return (mHistoryIterator = mHistory) != null;
+    }
+
+    public boolean getNextHistoryLocked(HistoryItem out) {
+        HistoryItem cur = mHistoryIterator;
+        if (cur == null) {
+            return false;
+        }
+        out.setTo(cur);
+        mHistoryIterator = cur.next;
+        return true;
+    }
+
     @Override
     public HistoryItem getHistory() {
         return mHistory;
@@ -3960,7 +4005,7 @@
             }
             if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
                 if (mFile != null) {
-                    writeLocked();
+                    writeAsyncLocked();
                 }
             }
         }
@@ -4356,11 +4401,22 @@
     }
 
     public void shutdownLocked() {
-        writeLocked();
+        writeSyncLocked();
         mShuttingDown = true;
     }
     
-    public void writeLocked() {
+    Parcel mPendingWrite = null;
+    final ReentrantLock mWriteLock = new ReentrantLock();
+
+    public void writeAsyncLocked() {
+        writeLocked(false);
+    }
+
+    public void writeSyncLocked() {
+        writeLocked(true);
+    }
+
+    void writeLocked(boolean sync) {
         if (mFile == null) {
             Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
             return;
@@ -4370,23 +4426,51 @@
             return;
         }
         
+        Parcel out = Parcel.obtain();
+        writeSummaryToParcel(out);
+        mLastWriteTime = SystemClock.elapsedRealtime();
+
+        if (mPendingWrite != null) {
+            mPendingWrite.recycle();
+        }
+        mPendingWrite = out;
+
+        if (sync) {
+            commitPendingDataToDisk();
+        } else {
+            Thread thr = new Thread("BatteryStats-Write") {
+                @Override
+                public void run() {
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                    commitPendingDataToDisk();
+                }
+            };
+            thr.start();
+        }
+    }
+
+    public void commitPendingDataToDisk() {
+        Parcel next;
+        synchronized (this) {
+            next = mPendingWrite;
+            mPendingWrite = null;
+
+            mWriteLock.lock();
+        }
+
         try {
             FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
-            Parcel out = Parcel.obtain();
-            writeSummaryToParcel(out);
-            stream.write(out.marshall());
-            out.recycle();
-
+            stream.write(next.marshall());
             stream.flush();
             stream.close();
             mFile.commit();
-
-            mLastWriteTime = SystemClock.elapsedRealtime();
-            return;
         } catch (IOException e) {
             Slog.w("BatteryStats", "Error writing battery statistics", e);
+            mFile.rollback();
+        } finally {
+            next.recycle();
+            mWriteLock.unlock();
         }
-        mFile.rollback();
     }
 
     static byte[] readFully(FileInputStream stream) throws java.io.IOException {
@@ -4630,7 +4714,7 @@
                         p.mSpeedBins[i].readSummaryFromParcelLocked(in);
                     }
                 }
-                p.readExcessiveWakeFromParcelLocked(in);
+                p.readExcessivePowerFromParcelLocked(in);
             }
 
             NP = in.readInt();
@@ -4829,7 +4913,7 @@
                             out.writeInt(0);
                         }
                     }
-                    ps.writeExcessiveWakeToParcelLocked(out);
+                    ps.writeExcessivePowerToParcelLocked(out);
                 }
             }
 
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index a038cc5..8c280b40 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -101,11 +101,12 @@
 	android_graphics_PixelFormat.cpp \
 	android/graphics/Picture.cpp \
 	android/graphics/PorterDuff.cpp \
-	android/graphics/LargeBitmap.cpp \
+	android/graphics/BitmapRegionDecoder.cpp \
 	android/graphics/Rasterizer.cpp \
 	android/graphics/Region.cpp \
 	android/graphics/Shader.cpp \
 	android/graphics/Typeface.cpp \
+	android/graphics/Utils.cpp \
 	android/graphics/Xfermode.cpp \
 	android/graphics/YuvToJpegEncoder.cpp \
 	android_media_AudioRecord.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 17ab46c..beb49c8 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,7 +53,7 @@
 extern int register_android_os_Process(JNIEnv* env);
 extern int register_android_graphics_Bitmap(JNIEnv*);
 extern int register_android_graphics_BitmapFactory(JNIEnv*);
-extern int register_android_graphics_LargeBitmap(JNIEnv*);
+extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
 extern int register_android_graphics_Camera(JNIEnv* env);
 extern int register_android_graphics_Graphics(JNIEnv* env);
 extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -1208,7 +1208,7 @@
 
     REG_JNI(register_android_graphics_Bitmap),
     REG_JNI(register_android_graphics_BitmapFactory),
-    REG_JNI(register_android_graphics_LargeBitmap),
+    REG_JNI(register_android_graphics_BitmapRegionDecoder),
     REG_JNI(register_android_graphics_Camera),
     REG_JNI(register_android_graphics_Canvas),
     REG_JNI(register_android_graphics_ColorFilter),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 6745b24..90a0243 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -10,6 +10,7 @@
 #include "SkUtils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "AutoDecodeCancel.h"
+#include "Utils.h"
 
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Asset.h>
@@ -99,57 +100,6 @@
     }
 };
 
-class AssetStreamAdaptor : public SkStream {
-public:
-    AssetStreamAdaptor(Asset* a) : fAsset(a) {}
-    
-    virtual bool rewind() {
-        off_t pos = fAsset->seek(0, SEEK_SET);
-        if (pos == (off_t)-1) {
-            SkDebugf("----- fAsset->seek(rewind) failed\n");
-            return false;
-        }
-        return true;
-    }
-    
-    virtual size_t read(void* buffer, size_t size) {
-        ssize_t amount;
-        
-        if (NULL == buffer) {
-            if (0 == size) {  // caller is asking us for our total length
-                return fAsset->getLength();
-            }
-            // asset->seek returns new total offset
-            // we want to return amount that was skipped
-
-            off_t oldOffset = fAsset->seek(0, SEEK_CUR);
-            if (-1 == oldOffset) {
-                SkDebugf("---- fAsset->seek(oldOffset) failed\n");
-                return 0;
-            }
-            off_t newOffset = fAsset->seek(size, SEEK_CUR);
-            if (-1 == newOffset) {
-                SkDebugf("---- fAsset->seek(%d) failed\n", size);
-                return 0;
-            }
-            amount = newOffset - oldOffset;
-        } else {
-            amount = fAsset->read(buffer, size);
-            if (amount <= 0) {
-                SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
-            }
-        }
-        
-        if (amount < 0) {
-            amount = 0;
-        }
-        return amount;
-    }
-    
-private:
-    Asset*  fAsset;
-};
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static inline int32_t validOrNeg1(bool isValid, int32_t value) {
@@ -201,12 +151,6 @@
             !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
 }
 
-static jobject nullObjectReturn(const char msg[]) {
-    if (msg) {
-        SkDebugf("--- %s\n", msg);
-    }
-    return NULL;
-}
 
 static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
                                    int sampleSize, bool ditherImage) {
@@ -377,23 +321,6 @@
     return size;
 }
 
-/** Restore the file descriptor's offset in our destructor
- */
-class AutoFDSeek {
-public:
-    AutoFDSeek(int fd) : fFD(fd) {
-        fCurr = ::lseek(fd, 0, SEEK_CUR);
-    }
-    ~AutoFDSeek() {
-        if (fCurr >= 0) {
-            ::lseek(fFD, fCurr, SEEK_SET);
-        }
-    }
-private:
-    int     fFD;
-    off_t   fCurr;
-};
-
 static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
                                           jobject fileDescriptor,
                                           jobject padding,
@@ -560,134 +487,6 @@
     }
 }
 
-static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
-    size_t bufferSize = 4096;
-    size_t streamLen = 0;
-    size_t len;
-    char* data = (char*)sk_malloc_throw(bufferSize);
-
-    while ((len = stream->read(data + streamLen,
-                    bufferSize - streamLen)) != 0) {
-        streamLen += len;
-        if (streamLen == bufferSize) {
-            bufferSize *= 2;
-            data = (char*)sk_realloc_throw(data, bufferSize);
-        }
-    }
-    data = (char*)sk_realloc_throw(data, streamLen);
-    SkMemoryStream* streamMem = new SkMemoryStream();
-    streamMem->setMemoryOwned(data, streamLen);
-    return streamMem;
-}
-
-static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
-    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
-    int width, height;
-    if (NULL == decoder) {
-        doThrowIOE(env, "Image format not supported");
-        return nullObjectReturn("SkImageDecoder::Factory returned null");
-    }
-
-    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
-    decoder->setAllocator(javaAllocator);
-    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
-    decoder->setReporter(javaMemoryReporter);
-    javaAllocator->unref();
-    javaMemoryReporter->unref();
-
-    if (!decoder->buildTileIndex(stream, &width, &height)) {
-        char msg[100];
-        snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
-                decoder->getFormatName());
-        doThrowIOE(env, msg);
-        return nullObjectReturn("decoder->buildTileIndex returned false");
-    }
-
-    SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
-
-    return GraphicsJNI::createLargeBitmap(env, bm);
-}
-
-static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
-                                     int offset, int length, jboolean isShareable) {
-    /*  If isShareable we could decide to just wrap the java array and
-        share it, but that means adding a globalref to the java array object
-        For now we just always copy the array's data if isShareable.
-     */
-    AutoJavaByteArray ar(env, byteArray);
-    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
-    return doBuildTileIndex(env, stream);
-}
-
-static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
-                                          jobject fileDescriptor, jboolean isShareable) {
-    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
-
-    jint descriptor = env->GetIntField(fileDescriptor,
-                                       gFileDescriptor_descriptor);
-    SkStream *stream = NULL;
-    struct stat fdStat;
-    int newFD;
-    if (fstat(descriptor, &fdStat) == -1) {
-        doThrowIOE(env, "broken file descriptor");
-        return nullObjectReturn("fstat return -1");
-    }
-
-    if (isShareable &&
-            S_ISREG(fdStat.st_mode) &&
-            (newFD = ::dup(descriptor)) != -1) {
-        SkFDStream* fdStream = new SkFDStream(newFD, true);
-        if (!fdStream->isValid()) {
-            fdStream->unref();
-            return NULL;
-        }
-        stream = fdStream;
-    } else {
-        SkFDStream* fdStream = new SkFDStream(descriptor, false);
-        if (!fdStream->isValid()) {
-            fdStream->unref();
-            return NULL;
-        }
-        stream = buildSkMemoryStream(fdStream);
-        fdStream->unref();
-    }
-
-    /* Restore our offset when we leave, so we can be called more than once
-       with the same descriptor. This is only required if we didn't dup the
-       file descriptor, but it is OK to do it all the time.
-    */
-    AutoFDSeek as(descriptor);
-
-    return doBuildTileIndex(env, stream);
-}
-
-static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
-                                  jobject is,       // InputStream
-                                  jbyteArray storage, // byte[]
-                                  jboolean isShareable) {
-    jobject largeBitmap = NULL;
-    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
-
-    if (stream) {
-        // for now we don't allow shareable with java inputstreams
-        SkMemoryStream *mStream = buildSkMemoryStream(stream);
-        largeBitmap = doBuildTileIndex(env, mStream);
-        stream->unref();
-    }
-    return largeBitmap;
-}
-
-static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
-                                 jint native_asset, // Asset
-                                 jboolean isShareable) {
-    SkStream* stream, *assStream;
-    Asset* asset = reinterpret_cast<Asset*>(native_asset);
-    assStream = new AssetStreamAdaptor(asset);
-    stream = buildSkMemoryStream(assStream);
-    assStream->unref();
-    return doBuildTileIndex(env, stream);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gMethods[] = {
@@ -717,26 +516,6 @@
     },
 
     {   "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
-
-    {   "nativeCreateLargeBitmap",
-        "([BIIZ)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromByteArray
-    },
-
-    {   "nativeCreateLargeBitmap",
-        "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromStream
-    },
-
-    {   "nativeCreateLargeBitmap",
-        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromFileDescriptor
-    },
-
-    {   "nativeCreateLargeBitmap",
-        "(IZ)Landroid/graphics/LargeBitmap;",
-        (void*)nativeCreateLargeBitmapFromAsset
-    },
 };
 
 static JNINativeMethod gOptionsMethods[] = {
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
new file mode 100644
index 0000000..4503852
--- /dev/null
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -0,0 +1,305 @@
+/*
+ * 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 "BitmapRegionDecoder"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "GraphicsJNI.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "SkStream.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkBitmapRegionDecoder.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "Utils.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <binder/Parcel.h>
+#include <jni.h>
+#include <utils/Asset.h>
+#include <sys/stat.h>
+
+static jclass gFileDescriptor_class;
+static jfieldID gFileDescriptor_descriptor;
+
+#if 0
+    #define TRACE_BITMAP(code)  code
+#else
+    #define TRACE_BITMAP(code)
+#endif
+
+using namespace android;
+
+static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
+    size_t bufferSize = 4096;
+    size_t streamLen = 0;
+    size_t len;
+    char* data = (char*)sk_malloc_throw(bufferSize);
+
+    while ((len = stream->read(data + streamLen,
+                    bufferSize - streamLen)) != 0) {
+        streamLen += len;
+        if (streamLen == bufferSize) {
+            bufferSize *= 2;
+            data = (char*)sk_realloc_throw(data, bufferSize);
+        }
+    }
+    data = (char*)sk_realloc_throw(data, streamLen);
+    SkMemoryStream* streamMem = new SkMemoryStream();
+    streamMem->setMemoryOwned(data, streamLen);
+    return streamMem;
+}
+
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
+    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+    int width, height;
+    if (NULL == decoder) {
+        doThrowIOE(env, "Image format not supported");
+        return nullObjectReturn("SkImageDecoder::Factory returned null");
+    }
+
+    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+    decoder->setAllocator(javaAllocator);
+    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+    decoder->setReporter(javaMemoryReporter);
+    javaAllocator->unref();
+    javaMemoryReporter->unref();
+
+    if (!decoder->buildTileIndex(stream, &width, &height)) {
+        char msg[100];
+        snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
+                decoder->getFormatName());
+        doThrowIOE(env, msg);
+        return nullObjectReturn("decoder->buildTileIndex returned false");
+    }
+
+    SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
+
+    return GraphicsJNI::createBitmapRegionDecoder(env, bm);
+}
+
+static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+                                     int offset, int length, jboolean isShareable) {
+    /*  If isShareable we could decide to just wrap the java array and
+        share it, but that means adding a globalref to the java array object
+        For now we just always copy the array's data if isShareable.
+     */
+    AutoJavaByteArray ar(env, byteArray);
+    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
+    return doBuildTileIndex(env, stream);
+}
+
+static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
+                                          jobject fileDescriptor, jboolean isShareable) {
+    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+    jint descriptor = env->GetIntField(fileDescriptor,
+                                       gFileDescriptor_descriptor);
+    SkStream *stream = NULL;
+    struct stat fdStat;
+    int newFD;
+    if (fstat(descriptor, &fdStat) == -1) {
+        doThrowIOE(env, "broken file descriptor");
+        return nullObjectReturn("fstat return -1");
+    }
+
+    if (isShareable &&
+            S_ISREG(fdStat.st_mode) &&
+            (newFD = ::dup(descriptor)) != -1) {
+        SkFDStream* fdStream = new SkFDStream(newFD, true);
+        if (!fdStream->isValid()) {
+            fdStream->unref();
+            return NULL;
+        }
+        stream = fdStream;
+    } else {
+        SkFDStream* fdStream = new SkFDStream(descriptor, false);
+        if (!fdStream->isValid()) {
+            fdStream->unref();
+            return NULL;
+        }
+        stream = buildSkMemoryStream(fdStream);
+        fdStream->unref();
+    }
+
+    /* Restore our offset when we leave, so we can be called more than once
+       with the same descriptor. This is only required if we didn't dup the
+       file descriptor, but it is OK to do it all the time.
+    */
+    AutoFDSeek as(descriptor);
+
+    return doBuildTileIndex(env, stream);
+}
+
+static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
+                                  jobject is,       // InputStream
+                                  jbyteArray storage, // byte[]
+                                  jboolean isShareable) {
+    jobject largeBitmap = NULL;
+    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+    if (stream) {
+        // for now we don't allow shareable with java inputstreams
+        SkMemoryStream *mStream = buildSkMemoryStream(stream);
+        largeBitmap = doBuildTileIndex(env, mStream);
+        stream->unref();
+    }
+    return largeBitmap;
+}
+
+static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
+                                 jint native_asset, // Asset
+                                 jboolean isShareable) {
+    SkStream* stream, *assStream;
+    Asset* asset = reinterpret_cast<Asset*>(native_asset);
+    assStream = new AssetStreamAdaptor(asset);
+    stream = buildSkMemoryStream(assStream);
+    assStream->unref();
+    return doBuildTileIndex(env, stream);
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
+        int start_x, int start_y, int width, int height, jobject options) {
+    SkImageDecoder *decoder = brd->getDecoder();
+    int sampleSize = 1;
+    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+    bool doDither = true;
+
+    if (NULL != options) {
+        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+        // initialize these, in case we fail later on
+        env->SetIntField(options, gOptions_widthFieldID, -1);
+        env->SetIntField(options, gOptions_heightFieldID, -1);
+        env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+    }
+
+    decoder->setDitherImage(doDither);
+    SkBitmap*           bitmap = new SkBitmap;
+    SkAutoTDelete<SkBitmap>       adb(bitmap);
+    AutoDecoderCancel   adc(options, decoder);
+
+    // To fix the race condition in case "requestCancelDecode"
+    // happens earlier than AutoDecoderCancel object is added
+    // to the gAutoDecoderCancelMutex linked list.
+    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+        return nullObjectReturn("gOptions_mCancelID");;
+    }
+
+    SkIRect region;
+    region.fLeft = start_x;
+    region.fTop = start_y;
+    region.fRight = start_x + width;
+    region.fBottom = start_y + height;
+
+    if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+        return nullObjectReturn("decoder->decodeRegion returned false");
+    }
+
+    // update options (if any)
+    if (NULL != options) {
+        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+        // TODO: set the mimeType field with the data from the codec.
+        // but how to reuse a set of strings, rather than allocating new one
+        // each time?
+        env->SetObjectField(options, gOptions_mimeFieldID,
+                            getMimeTypeString(env, decoder->getFormat()));
+    }
+
+    // detach bitmap from its autotdeleter, since we want to own it now
+    adb.detach();
+
+    SkPixelRef* pr;
+    pr = bitmap->pixelRef();
+    // promise we will never change our pixels (great for sharing and pictures)
+    pr->setImmutable();
+    // now create the java bitmap
+    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    return brd->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    return brd->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    delete brd;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gBitmapRegionDecoderMethods[] = {
+    {   "nativeDecodeRegion",
+        "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeRegion},
+
+    {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+
+    {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+
+    {   "nativeClean", "(I)V", (void*)nativeClean},
+
+    {   "nativeNewInstance",
+        "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromByteArray
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromStream
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromFileDescriptor
+    },
+
+    {   "nativeNewInstance",
+        "(IZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromAsset
+    },
+};
+
+#define kClassPathName  "android/graphics/BitmapRegionDecoder"
+
+int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
+int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
+{
+    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+            gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
+}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 72cea65..1ebe14b 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -168,8 +168,8 @@
 static jclass   gBitmapConfig_class;
 static jfieldID gBitmapConfig_nativeInstanceID;
 
-static jclass   gLargeBitmap_class;
-static jmethodID gLargeBitmap_constructorMethodID;
+static jclass   gBitmapRegionDecoder_class;
+static jmethodID gBitmapRegionDecoder_constructorMethodID;
 
 static jclass   gCanvas_class;
 static jfieldID gCanvas_nativeInstanceID;
@@ -376,17 +376,18 @@
     }
     return obj;
 }
-jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
+
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
 {
     SkASSERT(bitmap != NULL);
 
-    jobject obj = env->AllocObject(gLargeBitmap_class);
+    jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
     if (hasException(env)) {
         obj = NULL;
         return obj;
     }
     if (obj) {
-        env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
+        env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
         if (hasException(env)) {
             obj = NULL;
         }
@@ -612,8 +613,8 @@
     gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
                                             "(IZ[BI)V");
 
-    gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
-    gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
+    gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
+    gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
 
     gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
     gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
@@ -651,4 +652,3 @@
 
     return 0;
 }
-
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 1a43a3e..d0f9125 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,7 +4,7 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkBitmap.h"
-#include "../images/SkLargeBitmap.h"
+#include "../images/SkBitmapRegionDecoder.h"
 #include "../images/SkImageDecoder.h"
 #include <jni.h>
 
@@ -56,7 +56,7 @@
     
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
-    static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
+    static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
     /** Set a pixelref for the bitmap (needs setConfig to already be called)
         Returns true on success. If it returns false, then it failed, and the
@@ -181,4 +181,3 @@
     do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0)
 
 #endif
-
diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp
deleted file mode 100644
index 4cf5dfa..0000000
--- a/core/jni/android/graphics/LargeBitmap.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-#define LOG_TAG "LargeBitmap"
-
-#include "SkBitmap.h"
-#include "SkImageEncoder.h"
-#include "SkColorPriv.h"
-#include "GraphicsJNI.h"
-#include "SkDither.h"
-#include "SkUnPreMultiply.h"
-#include "SkUtils.h"
-#include "SkTemplates.h"
-#include "SkPixelRef.h"
-#include "BitmapFactory.h"
-#include "AutoDecodeCancel.h"
-#include "SkLargeBitmap.h"
-
-#include <binder/Parcel.h>
-#include "android_util_Binder.h"
-#include "android_nio_utils.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-
-#include <jni.h>
-
-#if 0
-    #define TRACE_BITMAP(code)  code
-#else
-    #define TRACE_BITMAP(code)
-#endif
-
-static jobject nullObjectReturn(const char msg[]) {
-    if (msg) {
-        SkDebugf("--- %s\n", msg);
-    }
-    return NULL;
-}
-
-/*
- * nine patch not supported
- *
- * purgeable not supported
- * reportSizeToVM not supported
- */
-static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
-        int start_x, int start_y, int width, int height, jobject options) {
-    SkImageDecoder *decoder = bm->getDecoder();
-    int sampleSize = 1;
-    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
-    bool doDither = true;
-
-    if (NULL != options) {
-        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
-        // initialize these, in case we fail later on
-        env->SetIntField(options, gOptions_widthFieldID, -1);
-        env->SetIntField(options, gOptions_heightFieldID, -1);
-        env->SetObjectField(options, gOptions_mimeFieldID, 0);
-
-        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
-        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
-        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
-    }
-
-    decoder->setDitherImage(doDither);
-    SkBitmap*           bitmap = new SkBitmap;
-    SkAutoTDelete<SkBitmap>       adb(bitmap);
-    AutoDecoderCancel   adc(options, decoder);
-
-    // To fix the race condition in case "requestCancelDecode"
-    // happens earlier than AutoDecoderCancel object is added
-    // to the gAutoDecoderCancelMutex linked list.
-    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
-        return nullObjectReturn("gOptions_mCancelID");;
-    }
-
-    SkIRect region;
-    region.fLeft = start_x;
-    region.fTop = start_y;
-    region.fRight = start_x + width;
-    region.fBottom = start_y + height;
-
-    if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
-        return nullObjectReturn("decoder->decodeRegion returned false");
-    }
-
-    // update options (if any)
-    if (NULL != options) {
-        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
-        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
-        // TODO: set the mimeType field with the data from the codec.
-        // but how to reuse a set of strings, rather than allocating new one
-        // each time?
-        env->SetObjectField(options, gOptions_mimeFieldID,
-                            getMimeTypeString(env, decoder->getFormat()));
-    }
-
-    // detach bitmap from its autotdeleter, since we want to own it now
-    adb.detach();
-
-    SkPixelRef* pr;
-    pr = bitmap->pixelRef();
-    // promise we will never change our pixels (great for sharing and pictures)
-    pr->setImmutable();
-    // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
-}
-
-static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
-    return bm->getHeight();
-}
-
-static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
-    return bm->getWidth();
-}
-
-static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
-    delete bm;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include <android_runtime/AndroidRuntime.h>
-
-static JNINativeMethod gLargeBitmapMethods[] = {
-    {   "nativeDecodeRegion",
-        "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
-        (void*)nativeDecodeRegion},
-    {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
-    {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
-    {   "nativeClean", "(I)V", (void*)nativeClean},
-};
-
-#define kClassPathName  "android/graphics/LargeBitmap"
-
-int register_android_graphics_LargeBitmap(JNIEnv* env);
-int register_android_graphics_LargeBitmap(JNIEnv* env)
-{
-    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
-                                gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
-}
-
diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp
new file mode 100644
index 0000000..b6ead19
--- /dev/null
+++ b/core/jni/android/graphics/Utils.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+#include "SkUtils.h"
+
+using namespace android;
+
+bool AssetStreamAdaptor::rewind() {
+    off_t pos = fAsset->seek(0, SEEK_SET);
+    if (pos == (off_t)-1) {
+        SkDebugf("----- fAsset->seek(rewind) failed\n");
+        return false;
+    }
+    return true;
+}
+
+size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
+    ssize_t amount;
+
+    if (NULL == buffer) {
+        if (0 == size) {  // caller is asking us for our total length
+            return fAsset->getLength();
+        }
+        // asset->seek returns new total offset
+        // we want to return amount that was skipped
+
+        off_t oldOffset = fAsset->seek(0, SEEK_CUR);
+        if (-1 == oldOffset) {
+            SkDebugf("---- fAsset->seek(oldOffset) failed\n");
+            return 0;
+        }
+        off_t newOffset = fAsset->seek(size, SEEK_CUR);
+        if (-1 == newOffset) {
+            SkDebugf("---- fAsset->seek(%d) failed\n", size);
+            return 0;
+        }
+        amount = newOffset - oldOffset;
+    } else {
+        amount = fAsset->read(buffer, size);
+        if (amount <= 0) {
+            SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
+        }
+    }
+
+    if (amount < 0) {
+        amount = 0;
+    }
+    return amount;
+}
+
+jobject android::nullObjectReturn(const char msg[]) {
+    if (msg) {
+        SkDebugf("--- %s\n", msg);
+    }
+    return NULL;
+}
diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h
new file mode 100644
index 0000000..2de41a1
--- /dev/null
+++ b/core/jni/android/graphics/Utils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_DEFINED
+#define UTILS_DEFINED
+
+#include "SkStream.h"
+
+#include "android_util_Binder.h"
+
+#include <jni.h>
+#include <utils/Asset.h>
+
+namespace android {
+
+class AssetStreamAdaptor : public SkStream {
+public:
+    AssetStreamAdaptor(Asset* a) : fAsset(a) {}
+    virtual bool rewind();
+    virtual size_t read(void* buffer, size_t size);
+
+private:
+    Asset*  fAsset;
+};
+
+
+/** Restore the file descriptor's offset in our destructor
+ */
+class AutoFDSeek {
+public:
+    AutoFDSeek(int fd) : fFD(fd) {
+        fCurr = ::lseek(fd, 0, SEEK_CUR);
+    }
+    ~AutoFDSeek() {
+        if (fCurr >= 0) {
+            ::lseek(fFD, fCurr, SEEK_SET);
+        }
+    }
+private:
+    int     fFD;
+    off_t   fCurr;
+};
+
+jobject nullObjectReturn(const char msg[]);
+
+}; // namespace android
+
+#endif
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 9d215b7..8409adc 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -252,21 +252,23 @@
     lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
     
     lpJniStorage->mStreamType = atStreamType;
-    
-    jint* nSession = NULL;
-    if (jSession) {
-        nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
-        if (nSession == NULL) {
-            LOGE("Error creating AudioTrack: Error retrieving session id pointer");
-            delete lpJniStorage;
-            return AUDIOTRACK_ERROR;
-        }
-    } else {
+
+    if (jSession == NULL) {
         LOGE("Error creating AudioTrack: invalid session ID pointer");
         delete lpJniStorage;
         return AUDIOTRACK_ERROR;
     }
 
+    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
+    if (nSession == NULL) {
+        LOGE("Error creating AudioTrack: Error retrieving session id pointer");
+        delete lpJniStorage;
+        return AUDIOTRACK_ERROR;
+    }
+    int sessionId = nSession[0];
+    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
+    nSession = NULL;
+
     // create the native AudioTrack object
     AudioTrack* lpTrack = new AudioTrack();
     if (lpTrack == NULL) {
@@ -288,7 +290,7 @@
             0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
             0,// shared mem
             true,// thread can call Java
-            nSession[0]);// audio session ID
+            sessionId);// audio session ID
             
     } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
         // AudioTrack is using shared memory
@@ -309,7 +311,7 @@
             0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 
             lpJniStorage->mMemBase,// shared mem
             true,// thread can call Java
-            nSession[0]);// audio session ID
+            sessionId);// audio session ID
     }
 
     if (lpTrack->initCheck() != NO_ERROR) {
@@ -317,9 +319,13 @@
         goto native_init_failure;
     }
 
+    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
+    if (nSession == NULL) {
+        LOGE("Error creating AudioTrack: Error retrieving session id pointer");
+        goto native_init_failure;
+    }
     // read the audio session ID back from AudioTrack in case we create a new session
     nSession[0] = lpTrack->getSessionId();
-
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ab0ff3f..68a5a14 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -242,6 +242,14 @@
         android:description="@string/permdesc_writeHistoryBookmarks"
         android:protectionLevel="dangerous" />
 
+    <!-- Allows an application to broadcast an Intent to set an alarm for the
+         user. -->
+    <permission android:name="com.android.alarm.permission.SET_ALARM"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:label="@string/permlab_setAlarm"
+        android:description="@string/permdesc_setAlarm"
+        android:protectionLevel="normal" />
+
     <!-- ======================================= -->
     <!-- Permissions for accessing location info -->
     <!-- ======================================= -->
diff --git a/core/res/res/drawable-hdpi/btn_circle_disable.png b/core/res/res/drawable-hdpi/btn_circle_disable.png
old mode 100755
new mode 100644
index 3f859cb..39652a8
--- a/core/res/res/drawable-hdpi/btn_circle_disable.png
+++ b/core/res/res/drawable-hdpi/btn_circle_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_disable_focused.png b/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
old mode 100755
new mode 100644
index 66251b8..1aa7ffe
--- a/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disable.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disable.9.png
index 606a68e..0d25b6e 100755
--- a/core/res/res/drawable-hdpi/btn_dropdown_disable.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disable_focused.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disable_focused.9.png
index d5d99cf..e21fd75 100755
--- a/core/res/res/drawable-hdpi/btn_dropdown_disable_focused.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
index c6503c7..0d25b6e 100644
--- a/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
index 152de8b..e21fd75 100644
--- a/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png b/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
index a4488b0..f10402f 100755
--- a/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png b/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
index bdb0077..366c6e0 100755
--- a/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png b/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
index 0033fdd..f063c8d 100755
--- a/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_ic_maximized.9.png b/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
old mode 100755
new mode 100644
index 0c19bb7..2ec27af
--- a/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
+++ b/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_ic_minimized.9.png b/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
old mode 100755
new mode 100644
index 2ec27af..0c19bb7
--- a/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
+++ b/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
index 0102a61..467a013 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_gray.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_gray.9.png
index 53ed136..e4199f6 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_gray.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_gray.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_green.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_green.9.png
index 6455790..51ecef0 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_green.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_green.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_red.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_red.9.png
index 49bb9c1..3672751 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_red.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_red.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_yellow.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_yellow.9.png
index b3c4c4c..b7c92cd 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_yellow.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_confirm_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png
index 00dea6ec..66eb5a6 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_pressed.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_pressed.9.png
index 45b1850..50ae93e 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_pressed.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_gray.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_gray.9.png
index 35b3529..59cada4 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_gray.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_gray.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_green.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_green.9.png
index 720de7f..66070bc 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_green.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_green.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_red.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_red.9.png
index b3387be..5a924cf 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_red.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_red.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_yellow.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_yellow.9.png
index 7ddfbcc..b5ff169 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_yellow.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_confirm_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png
index 1855e5f..ee1eab0 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_pressed.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_pressed.9.png
index 844f304..8c5a231 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_pressed.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png
old mode 100644
new mode 100755
index 3499208..9599fb5
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png
old mode 100644
new mode 100755
index 91eaec8..46d9ab3
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png
index 8818b9e..6c0dc0a 100644
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png
old mode 100644
new mode 100755
index e5bc5f6..3f9fb8f
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_normal.png b/core/res/res/drawable-hdpi/jog_tab_left_normal.png
old mode 100644
new mode 100755
index 5326c7c..8b89538
--- a/core/res/res/drawable-hdpi/jog_tab_left_normal.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_pressed.png b/core/res/res/drawable-hdpi/jog_tab_left_pressed.png
old mode 100644
new mode 100755
index 7b906df..ec98790
--- a/core/res/res/drawable-hdpi/jog_tab_left_pressed.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png
old mode 100644
new mode 100755
index ea8c315..2861e8d
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png
old mode 100644
new mode 100755
index aa0ceb9..e974bbc
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png
index d772fb6..9647fa6 100644
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png
old mode 100644
new mode 100755
index 3cfeb67..ad878e1
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_normal.png b/core/res/res/drawable-hdpi/jog_tab_right_normal.png
old mode 100644
new mode 100755
index da7726b..01bba0b
--- a/core/res/res/drawable-hdpi/jog_tab_right_normal.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_pressed.png b/core/res/res/drawable-hdpi/jog_tab_right_pressed.png
old mode 100644
new mode 100755
index 450a325..647e802
--- a/core/res/res/drawable-hdpi/jog_tab_right_pressed.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_dark.9.png b/core/res/res/drawable-hdpi/popup_center_dark.9.png
old mode 100755
new mode 100644
index e2e983f..ac1f92d
--- a/core/res/res/drawable-hdpi/popup_center_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_chat.png b/core/res/res/drawable-hdpi/stat_notify_chat.png
old mode 100755
new mode 100644
index 097a979..71ea8de
--- a/core/res/res/drawable-hdpi/stat_notify_chat.png
+++ b/core/res/res/drawable-hdpi/stat_notify_chat.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_email_generic.png b/core/res/res/drawable-hdpi/stat_notify_email_generic.png
old mode 100755
new mode 100644
index 78003fa..87f0cd4
--- a/core/res/res/drawable-hdpi/stat_notify_email_generic.png
+++ b/core/res/res/drawable-hdpi/stat_notify_email_generic.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_gmail.png b/core/res/res/drawable-hdpi/stat_notify_gmail.png
old mode 100755
new mode 100644
index 7356309..8a9140c
--- a/core/res/res/drawable-hdpi/stat_notify_gmail.png
+++ b/core/res/res/drawable-hdpi/stat_notify_gmail.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_usb_debugger.png b/core/res/res/drawable-hdpi/stat_notify_usb_debugger.png
deleted file mode 100755
index fdf6c6c..0000000
--- a/core/res/res/drawable-hdpi/stat_notify_usb_debugger.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb.png b/core/res/res/drawable-hdpi/stat_sys_adb.png
old mode 100644
new mode 100755
index aef8650..fdf6c6c
--- a/core/res/res/drawable-hdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png b/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png
deleted file mode 100755
index e306ad7..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_on.png b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
old mode 100755
new mode 100644
index 37fb8f7..f901ae8
--- a/core/res/res/drawable-hdpi/stat_sys_gps_on.png
+++ b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png b/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
index bdcb378..4fbfa4f 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
index 0876bc6..b07c7bc 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
index fa27ee4..de2f3c3 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
index 343e4ca..b5eab83 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/statusbar_background.9.png b/core/res/res/drawable-hdpi/statusbar_background.9.png
deleted file mode 100644
index 67c8b7f..0000000
--- a/core/res/res/drawable-hdpi/statusbar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png b/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
index f6e9a19..8540501 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png b/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
index 3bdf52d..9a50396 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png b/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
index 087956e..a0a3fef 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/expander_ic_maximized.9.png b/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
index 465cabd..d5c3276 100644
--- a/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
+++ b/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/expander_ic_minimized.9.png b/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
index 9967ecb..4515b42 100644
--- a/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
+++ b/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
index 4e88b37..8546c5f 100755
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png
index adbb146..4440abc 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png
index e8be7bf..26c07e6 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png
index 120a9d8..684f8ac 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png
index 60ec146..56cfce33a 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png
index 7477453..d4e1929 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png
index c79a35c..39cd1e5 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png
index 4ce09fa..a6ee329 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png
index 9d7565f..386ed9d 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png
index d5f9bd8..0242a42 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png
index 5b9c5b4..b8c2e18 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png
index 2e6ca2e..4c608ee 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png
index f41750d..8bdfd84 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png
old mode 100644
new mode 100755
index e8544ff..3dce451
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png
old mode 100644
new mode 100755
index d0ba8f8..829b146
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png
index 5188c86..f2ceb2e 100644
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png
old mode 100644
new mode 100755
index 861e17a..5a29262
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_normal.png b/core/res/res/drawable-mdpi/jog_tab_left_normal.png
old mode 100644
new mode 100755
index 7af1b85..eb91e97
--- a/core/res/res/drawable-mdpi/jog_tab_left_normal.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_pressed.png b/core/res/res/drawable-mdpi/jog_tab_left_pressed.png
old mode 100644
new mode 100755
index b76e83e..9951992
--- a/core/res/res/drawable-mdpi/jog_tab_left_pressed.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png
old mode 100644
new mode 100755
index 814a50d..d446480
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png
old mode 100644
new mode 100755
index cf157fc..96d7acb
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png
index 74f2935..2e1e105 100644
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png
old mode 100644
new mode 100755
index 6655731..8224c38
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_normal.png b/core/res/res/drawable-mdpi/jog_tab_right_normal.png
old mode 100644
new mode 100755
index 479c9a5..f2113f2
--- a/core/res/res/drawable-mdpi/jog_tab_right_normal.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_pressed.png b/core/res/res/drawable-mdpi/jog_tab_right_pressed.png
old mode 100644
new mode 100755
index 454aaf2..65cd51e
--- a/core/res/res/drawable-mdpi/jog_tab_right_pressed.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/overscroll_edge.png b/core/res/res/drawable-mdpi/overscroll_edge.png
new file mode 100644
index 0000000..22f4ef8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/overscroll_edge.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/overscroll_glow.png b/core/res/res/drawable-mdpi/overscroll_glow.png
new file mode 100644
index 0000000..761fb74
--- /dev/null
+++ b/core/res/res/drawable-mdpi/overscroll_glow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/statusbar_background.9.png b/core/res/res/drawable-mdpi/statusbar_background.9.png
deleted file mode 100644
index eb7c1a4..0000000
--- a/core/res/res/drawable-mdpi/statusbar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_middle.png b/core/res/res/drawable-mdpi/text_select_handle_middle.png
new file mode 100644
index 0000000..201e7f9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_select_handle_middle.png
Binary files differ
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index dda1fc5..ade22c7 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Umožňuje aplikaci uvolnit paměť telefonu smazáním souborů v adresáři mezipaměti aplikace. Přístup je velmi omezený, většinou pouze pro systémové procesy."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Přesun zdrojů aplikace"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Umožňuje aplikaci přesunout své zdroje z interní paměti na externí médium a opačně."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"čtení systémových souborů protokolu"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Umožňuje aplikaci číst různé systémové soubory protokolů. Toto nastavení aplikaci umožní získat obecné informace o činnostech s telefonem, ale neměly by obsahovat žádné osobní či soukromé informace."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"čtení systémových souborů protokolu"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Umožňuje aplikaci číst různé systémové soubory protokolů. Toto nastavení aplikaci umožní získat obecné informace o činnostech s telefonem, ale neměly by obsahovat žádné osobní či soukromé informace."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"čtení nebo zápis do prostředků funkce diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Umožňuje aplikaci číst libovolné prostředky ve skupině diag, např. soubory ve složce /dev, a zapisovat do nich. Může dojít k ovlivnění stability a bezpečnosti systému. Toto nastavení by měl používat pouze výrobce či operátor pro diagnostiku hardwaru."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"povolení či zakázání komponent aplikací"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Práce"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Jiné"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Vlastní"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"pomocí <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> pomocí <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Zadejte kód PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umožňuje aplikaci číst všechny navštívené adresy URL a záložky Prohlížeče."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zápis do historie a záložek Prohlížeče"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Umožní aplikaci změnit historii či záložky prohlížeče uložené v telefonu. Škodlivé aplikace mohou pomocí tohoto nastavení vymazat či pozměnit data Prohlížeče."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Změnit oprávnění prohlížeče poskytovat informace o zeměpisné poloze"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Umožňuje aplikaci změnit oprávnění prohlížeče poskytovat informace o zeměpisné poloze. Škodlivé aplikace mohou toto nastavení použít k odesílání informací o umístění na libovolné webové stránky."</string>
     <string name="save_password_message" msgid="767344687139195790">"Chcete, aby si prohlížeč zapamatoval toto heslo?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Vybrat vše"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Označit text"</string>
+    <string name="selectText" msgid="4862359311088898878">"Vybrat slovo"</string>
     <string name="cut" msgid="3092569408438626261">"Vyjmout"</string>
     <string name="copy" msgid="2681946229533511987">"Kopírovat"</string>
     <string name="paste" msgid="5629880836805036433">"Vložit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 7b3e82c..eb0fffc 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillader, at et program frigør plads på telefonen ved at slette filer i programmets cachemappe. Adgang er normalt meget begrænset til systemprocesser."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Flyt programressourcer"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillader, at et program flytter programressourcer fra interne til eksterne medier og omvendt."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"læs systemlogfiler"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Tillader, at et program læser fra systemets forskellige logfiler. Dermed kan generelle oplysninger om, hvad du laver med telefonen, registreres, men logfilerne bør ikke indeholde personlige eller private oplysninger."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"læs systemlogfiler"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Tillader, at et program læser fra systemets forskellige logfiler. Dermed kan generelle oplysninger om, hvad du laver med telefonen, registreres, men logfilerne bør ikke indeholde personlige eller private oplysninger."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"læs/skriv til ressourcer ejet af diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Tillader, at et program læser og skriver til alle ressourcer, der ejes af diag-gruppen, som f.eks. flier i /dev. Dette kan muligvis påvirke systemets stabilitet og sikkerhed. Dette bør KUN bruges til hardwarespecifikke diagnosticeringer foretaget af producent eller udbyder."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"aktiver eller deaktiver programkomponenter"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Arbejde"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Andre"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Tilpasset"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Indtast PIN-kode"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillader, at programmet læser alle de webadresser, browseren har besøgt, og alle browserens bogmærker."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriv browserens oversigt og bogmærker"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Tillader, at et program ændrer browseroversigten eller bogmærker, der er gemt på din telefon. Ondsindede programmer kan bruge dette til at slette eller ændre din browsers data."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Skift browsertilladelser for geografisk placering"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Giver et program tilladelse til at ændre browserens tilladelser for geografisk placering. Skadelige programmer kan bruge dette til at tillade, at placeringsoplysninger sendes til vilkårlige websteder."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ønsker du, at browseren skal huske denne adgangskode?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Vælg alle"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Marker tekst"</string>
+    <string name="selectText" msgid="4862359311088898878">"Vælg ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klip"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
     <string name="paste" msgid="5629880836805036433">"Indsæt"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4c5cc8c..bacb370 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Ermöglicht einer Anwendung, Telefonspeicher durch das Löschen von Dateien im Cache-Verzeichnis der Anwendung freizugeben. Der Zugriff beschränkt sich in der Regel auf Systemprozesse."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Anwendungsressourcen verschieben"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Ermöglicht einer Anwendung, Anwendungsressourcen von interne auf externe Medien zu verschieben und umgekehrt."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"System-Protokolldateien lesen"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Ermöglicht einer Anwendung, die verschiedenen Protokolldateien des Systems zu lesen. So können allgemeine Informationen zu den auf Ihrem Telefon durchgeführten Aktionen eingesehen werden, diese sollten jedoch keine persönlichen oder geheimen Daten enthalten."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"System-Protokolldateien lesen"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Ermöglicht einer Anwendung, die verschiedenen Protokolldateien des Systems zu lesen. So können allgemeine Informationen zu den auf Ihrem Telefon durchgeführten Aktionen eingesehen werden, diese sollten jedoch keine persönlichen oder geheimen Daten enthalten."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"Lese-/Schreibberechtigung für zu Diagnosegruppe gehörige Elemente"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Ermöglicht einer Anwendung, alle Elemente in der Diagnosegruppe zu lesen und zu bearbeiten, etwa Dateien in \"/dev\". Dies könnte eine potenzielle Gefährdung für die Stabilität und Sicherheit des Systems darstellen und sollte NUR für Hardware-spezifische Diagnosen des Herstellers oder Netzbetreibers verwendet werden."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"Anwendungskomponenten aktivieren oder deaktivieren"</string>
@@ -468,7 +468,7 @@
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
     <item msgid="7390473628275490700">"Windows Live"</item>
-    <item msgid="7882877134931458217">"Yahoo"</item>
+    <item msgid="7882877134931458217">"Yahoo!"</item>
     <item msgid="5035376313200585242">"Skype"</item>
     <item msgid="7532363178459444943">"QQ"</item>
     <item msgid="3713441034299660749">"Google Talk"</item>
@@ -515,7 +515,7 @@
     <string name="imProtocolCustom" msgid="6919453836618749992">"Benutzerdefiniert"</string>
     <string name="imProtocolAim" msgid="7050360612368383417">"AIM"</string>
     <string name="imProtocolMsn" msgid="144556545420769442">"Windows Live"</string>
-    <string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
+    <string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo!"</string>
     <string name="imProtocolSkype" msgid="9019296744622832951">"Skype"</string>
     <string name="imProtocolQq" msgid="8887484379494111884">"QQ"</string>
     <string name="imProtocolGoogleTalk" msgid="3808393979157698766">"Google Talk"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Beruflich"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Andere"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Benutzerdefiniert"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"über <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> über <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-Code eingeben"</string>
@@ -536,7 +544,7 @@
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
-    <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Schema für Entsperrung zeichnen"</string>
+    <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Muster zum Entsperren zeichnen"</string>
     <string name="lockscreen_emergency_call" msgid="5347633784401285225">"Notruf"</string>
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Zurück zum Anruf"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Ermöglicht der Anwendung, alle URLs, die mit dem Browser besucht wurden, sowie alle Lesezeichen des Browsers zu lesen."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Browserverlauf und Lesezeichen schreiben"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Ermöglicht einer Anwendung, den auf Ihrem Telefon gespeicherten Browserverlauf und die Lesezeichen zu ändern. Schädliche Anwendungen können so Ihre Browserdaten löschen oder ändern."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Geolokalisierungsberechtigungen des Browsers ändern"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Ermöglicht einer Anwendung, die Geolokalisierungsberechtigungen des Browsers zu ändern. Schädliche Anwendungen können dies nutzen, um das Senden von Standortinformationen an willkürliche Websites zuzulassen."</string>
     <string name="save_password_message" msgid="767344687139195790">"Möchten Sie, dass der Browser dieses Passwort speichert?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Alles auswählen"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Text auswählen"</string>
+    <string name="selectText" msgid="4862359311088898878">"Wort auswählen"</string>
     <string name="cut" msgid="3092569408438626261">"Ausschneiden"</string>
     <string name="copy" msgid="2681946229533511987">"Kopieren"</string>
     <string name="paste" msgid="5629880836805036433">"Einfügen"</string>
@@ -788,7 +800,7 @@
     <string name="usb_storage_button_mount" msgid="1052259930369508235">"USB-Speicher aktivieren"</string>
     <string name="usb_storage_error_message" msgid="2534784751603345363">"Bei der Verwendung Ihrer SD-Karte als USB-Speicher ist ein Problem aufgetreten."</string>
     <string name="usb_storage_notification_title" msgid="8175892554757216525">"USB-Verbindung"</string>
-    <string name="usb_storage_notification_message" msgid="7380082404288219341">"Wählen Sie die Dateien aus, die von Ihrem oder auf Ihren Computer kopiert werden sollen."</string>
+    <string name="usb_storage_notification_message" msgid="7380082404288219341">"Zum Kopieren von Dateien zu/von Ihrem Computer."</string>
     <string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"USB-Speicher deaktivieren"</string>
     <string name="usb_storage_stop_notification_message" msgid="2591813490269841539">"Auswählen, um USB-Speicher zu deaktivieren."</string>
     <string name="usb_storage_stop_title" msgid="660129851708775853">"USB-Speicher in Verwendung"</string>
@@ -809,14 +821,14 @@
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"Kandidaten"</u></string>
     <string name="ext_media_checking_notification_title" msgid="5457603418970994050">"SD-Karte wird vorbereitet"</string>
-    <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Nach Fehlern wird gesucht."</string>
+    <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Suche nach Fehlern"</string>
     <string name="ext_media_nofs_notification_title" msgid="780477838241212997">"SD-Karte leer"</string>
     <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD-Karte ist leer oder verfügt über ein nicht unterstütztes Dateisystem."</string>
     <string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Beschädigte SD-Karte"</string>
     <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"Die SD-Karte ist beschädigt. Sie müssen Ihre Karte eventuell neu formatieren."</string>
     <string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"SD-Karte unerwartet entfernt"</string>
     <string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"SD-Karte vor dem Entnehmen trennen, um Datenverlust zu vermeiden."</string>
-    <string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD-Karte\nkann entfernt werden."</string>
+    <string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD-Karte kann entfernt werden."</string>
     <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Die SD-Karte kann entfernt werden."</string>
     <string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"SD-Karte entfernt"</string>
     <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD-Karte entfernt. Legen Sie eine neue ein."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 45863e7..78bce1a 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Επιτρέπει σε μια εφαρμογή να αυξήσει τον ελεύθερο χώρο αποθήκευσης του τηλεφώνου διαγράφοντας αρχεία από τον κατάλογο προσωρινής μνήμης της εφαρμογής. Η πρόσβαση είναι συνήθως πολύ περιορισμένη στη διαδικασία συστήματος."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Μετακίνηση πόρων εφαρμογής"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Επιτρέπει σε μια εφαρμογή τη μετακίνηση πόρων εφαρμογής από ένα εσωτερικό σε ένα εξωτερικό μέσο και αντίστροφα."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"ανάγνωση αρχείων καταγραφής συστήματος"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Επιτρέπει σε μια εφαρμογή να αναγνώσει τα αρχεία καταγραφής του συστήματος. Έτσι μπορεί να ανακαλύψει γενικές πληροφορίες σχετικά με τις δραστηριότητές σας στο τηλέφωνο, όμως δεν θα πρέπει να περιέχουν προσωπικές ή ιδιωτικές πληροφορίες."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"ανάγνωση αρχείων καταγραφής συστήματος"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Επιτρέπει σε μια εφαρμογή να αναγνώσει τα αρχεία καταγραφής του συστήματος. Έτσι μπορεί να ανακαλύψει γενικές πληροφορίες σχετικά με τις δραστηριότητές σας στο τηλέφωνο, όμως δεν θα πρέπει να περιέχουν προσωπικές ή ιδιωτικές πληροφορίες."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"ανάγνωση/εγγραφή σε πόρους που ανήκουν στο διαγνωστικό"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Επιτρέπει σε μια εφαρμογή την ανάγνωση και την εγγραφή σε πόρο που ανήκει στην ομάδα διαγνωστικού (π.χ. αρχεία στον κατάλογο /dev). Αυτό ενδέχεται να επηρεάσει την σταθερότητα και την ασφάλεια του συστήματος. Θα πρέπει να χρησιμοποιείται ΜΟΝΟ για διαγνωστικά υλικού του κατασκευαστή ή του χειριστή."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"ενεργοποίηση ή απενεργοποίηση στοιχείων εφαρμογής"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Εργασία"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Άλλο"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Προσαρμοσμένο"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"μέσω <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> μέσω <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Πληκτρολογήστε τον κωδικό αριθμό PIN"</string>
@@ -555,7 +563,7 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Η κάρτα SIM είναι κλειδωμένη."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Ξεκλείδωμα κάρτας SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος<xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Προσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google."\n\n" Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> \nδευτερόλεπτα."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google."\n\n" Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g>  δευτερόλεπτα."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Προσπαθήστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Ξεχάσατε το μοτίβο;"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Ξεκλείδωμα λογαριασμού"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των διευθύνσεων URL που το πρόγραμμα περιήγησης έχει επισκεφθεί και όλων των σελιδοδεικτών του προγράμματος περιήγησης."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"εγγραφή ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Επιτρέπει σε μια εφαρμογή να τροποποιήσει το ιστορικό ή τους σελιδοδείκτες του προγράμματος περιήγησης που βρίσκονται αποθηκευμένα στο τηλέφωνό σας. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να διαγράψουν ή να τροποποιήσουν τα δεδομένα του προγράμματος περιήγησης."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Τροποποίηση δικαιωμάτων γεωγραφικής θέσης προγράμματος περιήγησης"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Επιτρέπει σε μια εφαρμογή την τροποποίηση των δικαιωμάτων γεωγραφικής θέσης του προγράμματος περιήγησης. Οι κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να επιτρέψουν την αποστολή στοιχείων τοποθεσίας σε αυθαίρετους ιστότοπους."</string>
     <string name="save_password_message" msgid="767344687139195790">"Θέλετε το πρόγραμμα περιήγησης να διατηρήσει αυτόν τον κωδικό πρόσβασης;"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Επιλογή όλων"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Επιλογή κειμένου"</string>
+    <string name="selectText" msgid="4862359311088898878">"Επιλογή λέξης"</string>
     <string name="cut" msgid="3092569408438626261">"Αποκοπή"</string>
     <string name="copy" msgid="2681946229533511987">"Αντιγραφή"</string>
     <string name="paste" msgid="5629880836805036433">"Επικόλληση"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2765e96..6615e70 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Admite una aplicación que libera espacio de almacenamiento en el teléfono al eliminar archivos del directorio de memoria caché de la aplicación. En general, el acceso es muy restringido para el proceso del sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de la aplicación"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite a una aplicación mover recursos de aplicación de medios internos a externos y viceversa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Admite una aplicación que lee diversos archivos de registro del sistema. Esto le permite descubrir información general sobre lo que haces con el teléfono, pero no debe contener información personal ni privada."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"leer archivos de registro del sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Admite una aplicación que lee diversos archivos de registro del sistema. Esto le permite descubrir información general sobre lo que haces con el teléfono, pero no debe contener información personal ni privada."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"leer y escribir a recursos dentro del grupo de diagnóstico"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Admite una aplicación que lee y escribe a cualquier recurso dentro del grupo de diagnóstico; por ejemplo, archivos con /dev. Esto puede afectar potencialmente la estabilidad y la seguridad del sistema. Debe utilizarlo SÓLO el fabricante o el operador en los diagnósticos específicos del hardware."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"activar o desactivar componentes de la aplicación"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Trabajo"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Otro"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ingresar el código de PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite a la aplicación leer todas las URL que ha visitado el navegador y todos los marcadores del navegador."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir historial y marcadores del navegador"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite a una aplicación modificar el historial y los marcadores del navegador almacenados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar tus datos."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modificar los permisos de ubicación geográfica del navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite que una aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones maliciosas pueden utilizarlos para permitir el envío de información sobre la ubicación a sitos web de forma arbitraria."</string>
     <string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar todos"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleccionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleccionar palabra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
@@ -863,6 +875,6 @@
     <string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Amplia utilización de datos móviles"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toca para obtener más información acerca de la utilización de datos móviles."</string>
-    <string name="throttled_notification_title" msgid="6269541897729781332">"Límite de datos móviles excedido "</string>
+    <string name="throttled_notification_title" msgid="6269541897729781332">"Límite de datos móviles excedido"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Toca para obtener más información acerca de la utilización de datos móviles."</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 0d2f932..ae59862 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que una aplicación libere espacio de almacenamiento en el teléfono mediante la eliminación de archivos en el directorio de caché de la aplicación. El acceso al proceso del sistema suele estar muy restringido."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicaciones"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que una aplicación mueva los recursos de aplicaciones de un medio interno a otro externo y viceversa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que una aplicación lea los distintos archivos de registro del sistema. Con este permiso, la aplicación puede ver información general sobre las acciones que realiza el usuario con el teléfono, pero los registros no deberían contener información personal o privada."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"leer archivos de registro del sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permite que una aplicación lea los distintos archivos de registro del sistema. Con este permiso, la aplicación puede ver información general sobre las acciones que realiza el usuario con el teléfono, pero los registros no deberían contener información personal o privada."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"leer/escribir en los recursos propiedad del grupo de diagnóstico"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite que una aplicación lea y escriba en cualquier recurso propiedad del grupo de diagnóstico como, por ejemplo, archivos in/dev. Este permiso podría afectar a la seguridad y estabilidad del sistema. SÓLO se debe utilizar para diagnósticos específicos de hardware realizados por el fabricante o el operador."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"habilitar o inhabilitar componentes de la aplicación"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Trabajo"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Otra"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizada"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduce el código PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que la aplicación lea todas las URL que ha visitado el navegador y todos sus marcadores."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir en marcadores y en el historial del navegador"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que una aplicación modifique la información de los marcadores o del historial del navegador almacenada en el teléfono. Las aplicaciones malintencionadas pueden utilizar este permiso para borrar o modificar los datos del navegador."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modificar los permisos de ubicación geográfica del navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite que una aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones malintencionadas pueden utilizar este permiso para permitir el envío de información sobre la ubicación a sitios web arbitrarios."</string>
     <string name="save_password_message" msgid="767344687139195790">"¿Deseas que el navegador recuerde esta contraseña?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar todo"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleccionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleccionar palabra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b4f8046..9dd1a11 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permet à une application de libérer de l\'espace dans la mémoire du téléphone en supprimant des fichiers du répertoire du cache des applications. Cet accès est en général limité aux processus système."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Déplacer des ressources d\'application"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Autorise l\'application à déplacer des ressources d\'application d\'un support interne à un support externe et inversement."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"Lecture des fichiers journaux du système"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permet à une application de lire les différents fichiers journaux du système afin d\'obtenir des informations générales sur la façon dont vous utilisez votre téléphone,  sans pour autant récupérer des informations d\'ordre personnel ou privé."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"Lecture des fichiers journaux du système"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permet à une application de lire les différents fichiers journaux du système afin d\'obtenir des informations générales sur la façon dont vous utilisez votre téléphone,  sans pour autant récupérer des informations d\'ordre personnel ou privé."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"Lecture/écriture dans les ressources appartenant aux diagnostics"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permet à une application de lire et d\'éditer toute ressource appartenant au groupe de diagnostics (par exemple, les fichiers in/dev). Ceci peut affecter la stabilité et la sécurité du système. Cette fonctionnalité est UNIQUEMENT réservée aux diagnostics matériels effectués par le fabricant ou l\'opérateur."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"Activer ou désactiver des éléments de l\'application"</string>
@@ -434,7 +434,7 @@
     <string name="policydesc_wipeData" msgid="2314060933796396205">"Rétablit les paramètres d\'usine, supprimant toutes vos données sans demande de confirmation."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domicile"</item>
-    <item msgid="869923650527136615">"Portable"</item>
+    <item msgid="869923650527136615">"Mobile"</item>
     <item msgid="7897544654242874543">"Bureau"</item>
     <item msgid="1103601433382158155">"Télécopie bureau"</item>
     <item msgid="1735177144948329370">"Télécopie domicile"</item>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Bureau"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Autre"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personnalisé"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Saisissez le code PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Autorise l\'application à lire toutes les URL auxquelles le navigateur a accédé et tous ses favoris."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"écrire dans l\'historique et les favoris du navigateur"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Autorise une application à modifier l\'historique du navigateur ou les favoris enregistrés sur votre téléphone. Des applications malveillantes peuvent utiliser cette fonction pour effacer ou modifier les données de votre navigateur."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modifier les autorisations de géolocalisation du navigateur"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permet à une application de modifier les autorisations de géolocalisation du navigateur. Les applications malveillantes peuvent se servir de cette fonctionnalité pour envoyer des informations de lieu à des sites Web arbitraires."</string>
     <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Tout sélectionner"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Sélectionner le texte"</string>
+    <string name="selectText" msgid="4862359311088898878">"Sélectionner texte"</string>
     <string name="cut" msgid="3092569408438626261">"Couper"</string>
     <string name="copy" msgid="2681946229533511987">"Copier"</string>
     <string name="paste" msgid="5629880836805036433">"Coller"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index d4019b6..4c4f8f1 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Consente a un\'applicazione di liberare spazio sul telefono eliminando file nella directory della cache dell\'applicazione. L\'accesso è generalmente limitato a processi di sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Spostare risorse dell\'applicazione"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Consente a un\'applicazione di spostare risorse applicative da supporti interni a esterni e viceversa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"lettura file di registro sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Consente a un\'applicazione di leggere vari file di registro del sistema per trovare informazioni generali sulle operazioni effettuate con il telefono. Tali file non dovrebbero contenere informazioni personali o riservate."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"lettura file di registro sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Consente a un\'applicazione di leggere vari file di registro del sistema per trovare informazioni generali sulle operazioni effettuate con il telefono. Tali file non dovrebbero contenere informazioni personali o riservate."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"lettura/scrittura risorse di proprietà di diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Consente a un\'applicazione di leggere le risorse del gruppo diag e scrivere a esse, per esempio i file in /dev. Questa capacità potrebbe influire sulla stabilità e sicurezza del sistema. Dovrebbe essere utilizzata SOLTANTO per diagnostiche specifiche dell\'hardware effettuate dal produttore o dall\'operatore."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"attivazione/disattivazione componenti applicazioni"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Lavoro"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Altro"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizzato"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"tramite <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> tramite <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Inserisci il PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Consente all\'applicazione di leggere tutti gli URL visitati e tutti i segnalibri del browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"creazione cronologia e segnalibri del browser"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Consente a un\'applicazione di modificare la cronologia o i segnalibri del browser memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per cancellare o modificare i dati del browser."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modifica le autorizzazioni di localizzazione geografica del browser"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Consente a un\'applicazione di modificare le autorizzazioni di localizzazione geografica del browser. Le applicazioni dannose possono utilizzare questa autorizzazione per consentire l\'invio di informazioni sulla posizione a siti web arbitrari."</string>
     <string name="save_password_message" msgid="767344687139195790">"Memorizzare la password nel browser?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleziona tutto"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleziona testo"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleziona parola"</string>
     <string name="cut" msgid="3092569408438626261">"Taglia"</string>
     <string name="copy" msgid="2681946229533511987">"Copia"</string>
     <string name="paste" msgid="5629880836805036433">"Incolla"</string>
@@ -744,7 +756,7 @@
     <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Seleziona per passare all\'applicazione"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Scambiare le applicazioni?"</string>
     <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Un\'altra applicazione già in esecuzione deve essere chiusa prima di poterne avviare un\'altra."</string>
-    <string name="old_app_action" msgid="493129172238566282">"Torna a <xliff:g id="OLD_APP">%1$s</xliff:g> "</string>
+    <string name="old_app_action" msgid="493129172238566282">"Torna a <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="old_app_description" msgid="942967900237208466">"Non avviare la nuova applicazione."</string>
     <string name="new_app_action" msgid="5472756926945440706">"Avvia <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="new_app_description" msgid="6830398339826789493">"Interrompi la vecchia applicazione senza salvare."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ae44bb2..094b469 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"アプリケーションのキャッシュディレクトリからファイルを削除して携帯電話のメモリを解放することをアプリケーションに許可します。通常、アクセスはシステムプロセスのみに制限されます。"</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"アプリケーションリソースの移動"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"内部と外部のメディア間でのアプリケーションリソースの移動をアプリケーションに許可します。"</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"システムログファイルの読み取り"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"システムのさまざまなログファイルの読み取りをアプリケーションに許可します。これにより携帯電話の使用状況に関する全般情報が取得されますが、個人情報や非公開情報が含まれることはありません。"</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"システムログファイルの読み取り"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"システムのさまざまなログファイルの読み取りをアプリケーションに許可します。これにより携帯電話の使用状況に関する全般情報が取得されますが、個人情報や非公開情報が含まれることはありません。"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"diagが所有するリソースの読み書き"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"diagグループが所有するリソース(例:/dev内のファイル)への読み書きをアプリケーションに許可します。システムの安定性とセキュリティに影響する恐れがあります。メーカー/オペレーターによるハードウェア固有の診断以外には使用しないでください。"</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"アプリケーションのコンポーネントを有効/無効にする"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"勤務先"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"その他"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"カスタム"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g>経由"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>、更新元: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PINコードを入力"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"ブラウザでアクセスしたすべてのURLおよびブラウザのすべてのブックマークの読み取りをアプリケーションに許可します。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"ブラウザの履歴とブックマークを書き込む"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"携帯電話に保存されているブラウザの履歴やブックマークの修正をアプリケーショに許可します。これにより悪意のあるアプリケーションが、ブラウザのデータを消去または変更する恐れがあります。"</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"ブラウザの位置情報へのアクセス権を変更"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"ブラウザの位置情報に対するアクセス権の変更をアプリケーションに許可します。この設定では、悪意のあるアプリケーションが任意のウェブサイトに位置情報を送信する可能性があります。"</string>
     <string name="save_password_message" msgid="767344687139195790">"このパスワードをブラウザで保存しますか?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"すべて選択"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"テキストを選択"</string>
+    <string name="selectText" msgid="4862359311088898878">"語句を選択"</string>
     <string name="cut" msgid="3092569408438626261">"切り取り"</string>
     <string name="copy" msgid="2681946229533511987">"コピー"</string>
     <string name="paste" msgid="5629880836805036433">"貼り付け"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 040f9e8..b16325a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"애플리케이션이 애플리케이션 캐시 디렉토리에 있는 파일을 삭제하여 휴대전화의 저장공간을 늘릴 수 있도록 합니다. 액세스는 일반적으로 시스템 프로세스로 제한됩니다."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"애플리케이션 리소스 이동"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"애플리케이션이 애플리케이션 리소스를 내부에서 외부 미디어로 또는 그 반대로 이동할 수 있도록 합니다."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"시스템 로그 파일 읽기"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"애플리케이션이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 애플리케이션은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있습니다. 하지만 로그 파일에 어떠한 개인정보도 포함되어서는 안 됩니다."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"시스템 로그 파일 읽기"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"애플리케이션이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 애플리케이션은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있습니다. 하지만 로그 파일에 어떠한 개인정보도 포함되어서는 안 됩니다."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"진단 그룹 소유의 리소스 읽기/쓰기"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"애플리케이션이 진단 그룹 소유의 리소스(예: /dev에 있는 파일)를 읽고 쓸 수 있도록 합니다. 이 기능은 시스템 안정성 및 보안에 영향을 미칠 수 있으므로 제조업체 또는 사업자가 하드웨어 관련 진단을 수행하는 경우에만 사용해야 합니다."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"애플리케이션 구성 요소 사용 또는 사용 안함"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"직장"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"기타"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"맞춤설정"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g>을(를) 통해"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>(<xliff:g id="SOURCE">%2$s</xliff:g> 사용)"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN 코드 입력"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"애플리케이션이 브라우저로 방문한 모든 URL과 브라우저의 모든 북마크를 읽도록 허용합니다."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"브라우저의 기록 및 북마크 쓰기"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"애플리케이션이 휴대전화에 저장된 브라우저 기록 또는 북마크를 수정할 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 브라우저의 데이터를 지우거나 수정할 수 있습니다."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"브라우저 위치 정보 수정 권한"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"애플리케이션이 브라우저의 위치 정보 권한을 수정할 수 있도록 합니다. 악성 애플리케이션이 이를 사용하여 임의의 웹사이트에 위치 정보를 보낼 수도 있습니다."</string>
     <string name="save_password_message" msgid="767344687139195790">"브라우저에 이 비밀번호를 저장하시겠습니까?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"모두 선택"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"텍스트 선택"</string>
+    <string name="selectText" msgid="4862359311088898878">"단어 선택"</string>
     <string name="cut" msgid="3092569408438626261">"잘라내기"</string>
     <string name="copy" msgid="2681946229533511987">"복사"</string>
     <string name="paste" msgid="5629880836805036433">"붙여넣기"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 4ca84ed..2ba3e9d 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Lar applikasjonen frigjøre lagringsplass ved å slette filer i applikasjoners hurtigbufferkatalog. Tilgangen er vanligvis sterkt begrenset, til systemprosesser."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Flytter programressurser"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Gir et program tillatelse til å flytte programressurser fra interne til eksterne medier og omvendt."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"lese systemets loggfiler"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Lar applikasjonen lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"lese systemets loggfiler"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Lar applikasjonen lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"lese/skrive ressurser eid av diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Lar applikasjonen lese og skrive enhver ressurs eid av gruppen diag; for eksempel, filer i /dev. Dette kan potensielt påvirke systemets sikkerhet og stabilitet. Dette bør KUN brukes for maskinvarespesifikke diagnoseverktøy laget av operatøren eller produsenten."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"aktivere eller deaktigere applikasjonskomponenter"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Arbeid"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Annen"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Egendefinert"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Skriv inn PIN-kode:"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Lar applikasjonen lese alle adresser nettleseren har besøkt, og alle nettleserens bokmerker."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive til nettleserens logg og bokmerker"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Lar applikasjonen endre nettleserens logg og bokmerker lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å fjerne eller redigere nettleserens data."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Endre nettleserens tillatelser for geografisk posisjonering"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Tillater programmet å endre nettleserens tillatelser for geografisk posisjonering. Skadelige programmer kan bruke denne funksjonen til å sende posisjonsopplysninger til vilkårlige nettsteder."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ønsker du at nettleseren skal huske dette passordet?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Merk alt"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Merk tekst"</string>
+    <string name="selectText" msgid="4862359311088898878">"Velg ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
     <string name="paste" msgid="5629880836805036433">"Lim inn"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c8f0068..aa95374 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Hiermee kan een toepassing opslagruimte op de telefoon vrij maken door bestanden te verwijderen uit de cachemap van de toepassing. De toegang is doorgaans beperkt tot het systeemproces."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Toepassingsbronnen verplaatsen"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Een toepassing toestaan toepassingsbronnen te verplaatsen van interne naar externe media en omgekeerd."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"systeemlogbestanden lezen"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Hiermee kan een toepassing de verschillende logbestanden van het systeem lezen. De toepassing kan op deze manier algemene informatie achterhalen over uw telefoongebruik. Hierin is geen persoonlijke of privé-informatie opgenomen."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"systeemlogbestanden lezen"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Hiermee kan een toepassing de verschillende logbestanden van het systeem lezen. De toepassing kan op deze manier algemene informatie achterhalen over uw telefoongebruik. Hierin is geen persoonlijke of privé-informatie opgenomen."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"lezen/schrijven naar bronnen van diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Hiermee kan een toepassing lezen en schrijven naar elke bron die hoort bij de diagnostische groep, zoals bestanden in /dev. Hierdoor kan de systeemstabiliteit en -veiligheid worden beïnvloed. Dit mag ALLEEN worden gebruikt voor hardwarespecifieke diagnostiek door de fabrikant of operator."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"toepassingscomponenten in- of uitschakelen"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Werk"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Overig"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Aangepast"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-code invoeren"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Hiermee kan een toepassing de URL\'s lezen die u via de browser heeft bezocht, evenals alle bladwijzers van de browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"browsergeschiedenis en bladwijzers schrijven"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Hiermee kan een toepassing de op uw telefoon opgeslagen browsergeschiedenis of bladwijzers wijzigen. Schadelijke toepassingen kunnen hiermee uw browsergegevens verwijderen of wijzigen."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Geolocatierechten voor browser aanpassen"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Staat een toepassing toe de geolocatierechten van de browser aan te passen. Schadelijke toepassingen kunnen dit gebruiken om locatiegegevens te verzenden naar willekeurige websites."</string>
     <string name="save_password_message" msgid="767344687139195790">"Wilt u dat de browser dit wachtwoord onthoudt?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Alles selecteren"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Tekst selecteren"</string>
+    <string name="selectText" msgid="4862359311088898878">"Woord selecteren"</string>
     <string name="cut" msgid="3092569408438626261">"Knippen"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiëren"</string>
     <string name="paste" msgid="5629880836805036433">"Plakken"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ae897a8..047a0c1 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Pozwala aplikacji na zwalnianie pamięci telefonu przez usuwanie plików z katalogu pamięci podręcznej aplikacji. Dostęp jest bardzo ograniczony, z reguły do procesów systemu."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Przenoszenie zasobów aplikacji"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Zezwala aplikacji na przeniesienie zasobów aplikacji z nośnika wewnętrznego na zewnętrzny i odwrotnie."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"czytanie plików dziennika systemu"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Umożliwia aplikacji czytanie różnych plików dziennika systemowego. Pozwala to na uzyskanie ogólnych informacji o czynnościach wykonywanych w telefonie, ale bez ujawniania danych osobowych lub osobistych informacji."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"czytanie plików dziennika systemu"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Umożliwia aplikacji czytanie różnych plików dziennika systemowego. Pozwala to na uzyskanie ogólnych informacji o czynnościach wykonywanych w telefonie, ale bez ujawniania danych osobowych lub osobistych informacji."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"czytanie/zapisywanie w zasobach należących do diagnostyki"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Pozwala aplikacji na czytanie i zapisywanie we wszystkich zasobach posiadanych przez diagnozowaną grupę, jak na przykład pliki w katalogu /dev. Może to potencjalnie wpłynąć na stabilność i bezpieczeństwo systemu. Powinno być wykorzystywane TYLKO w celach diagnozowania sprzętu przez producenta lub operatora."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"włączanie lub wyłączanie składników aplikacji"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Służbowy"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Inny"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Niestandardowy"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"przez <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> przez <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Wprowadź kod PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umożliwia aplikacji odczyt wszystkich adresów URL odwiedzonych przez przeglądarkę, a także wszystkich zakładek przeglądarki."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zapis historii i zakładek przeglądarki"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Umożliwia aplikacji modyfikowanie historii lub zakładek przeglądarki zapisanych w telefonie. Złośliwe aplikacje mogą używać tej opcji do usuwania lub modyfikowania danych przeglądarki."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modyfikowanie uprawnień przeglądarki dotyczących lokalizacji geograficznej"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Zezwala aplikacji na modyfikowanie uprawnień przeglądarki dotyczących lokalizacji geograficznej. Złośliwe aplikacje mogą używać tej opcji do wysyłania informacji o lokalizacji do dowolnych witryn internetowych."</string>
     <string name="save_password_message" msgid="767344687139195790">"Czy chcesz, aby zapamiętać to hasło w przeglądarce?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Zaznacz wszystko"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Zaznacz tekst"</string>
+    <string name="selectText" msgid="4862359311088898878">"Zaznacz wyraz"</string>
     <string name="cut" msgid="3092569408438626261">"Wytnij"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiuj"</string>
     <string name="paste" msgid="5629880836805036433">"Wklej"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index b9f9be2..15cb833 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite a uma aplicação libertar espaço de armazenamento no telefone eliminando ficheiros no directório da cache da aplicação. Geralmente, o acesso é muito limitado para processamento do sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicações"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que uma aplicação mova recursos de aplicações de meios internos para meios externos e vice-versa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"ler ficheiros de registo do sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite a uma aplicação ler a partir dos diversos ficheiros de registo do sistema. Isto permite descobrir informações gerais sobre a forma como o utilizador usa o telefone, mas estas não devem conter quaisquer dados pessoais ou privados."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"ler ficheiros de registo do sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permite a uma aplicação ler a partir dos diversos ficheiros de registo do sistema. Isto permite descobrir informações gerais sobre a forma como o utilizador usa o telefone, mas estas não devem conter quaisquer dados pessoais ou privados."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"ler/escrever em recursos propriedade de diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite a uma aplicação ler e escrever em qualquer recurso que seja propriedade do grupo diag. Por exemplo, ficheiros em /dev. Isto pode afectar potencialmente a estabilidade e a segurança do sistema e deve ser utilizado APENAS para diagnósticos específicos do hardware pelo fabricante ou pelo operador."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"activar ou desactivar componentes da aplicação"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Emprego"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Outro"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"através do <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> através de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduzir código PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que a aplicação leia todos os URLs visitados pelo browser e todos os marcadores do browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e marcadores do browser"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que uma aplicação modifique o histórico e os marcadores do browser armazenados no telefone. As aplicações maliciosas podem utilizar esta permissão para apagar ou modificar os dados do browser."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modificar permissões de localização geográfica do Navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite a uma aplicação modificar as permissões de localização geográfica do Navegador. As aplicações mal intencionadas podem utilizar isto para enviar informações de localização para Web sites arbitrários."</string>
     <string name="save_password_message" msgid="767344687139195790">"Quer que o browser memorize esta palavra-passe?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar tudo"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleccionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleccionar palavra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
@@ -782,7 +794,7 @@
     <string name="no_permissions" msgid="7283357728219338112">"Não são necessárias permissões"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar tudo"</b></string>
-    <string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento em massa USB "</string>
+    <string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento em massa USB"</string>
     <string name="usb_storage_title" msgid="5901459041398751495">"Ligado através de USB"</string>
     <string name="usb_storage_message" msgid="4796759646167247178">"Ligou o telefone ao computador através de USB. Seleccione o botão abaixo se pretender copiar ficheiros entre o computador e o cartão SD do Android."</string>
     <string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar armazenamento USB"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 7cc266a..4f9258a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que um aplicativo libere o espaço de armazenamento do telefone excluindo arquivos no diretório de cache do aplicativo. O acesso é normalmente muito restrito para o processo do sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos do aplicativo"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que um aplicativo mova os recursos do aplicativo da mídia interna para a externa e vice-versa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"ler arquivos de registro do sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, porém esses arquivos não devem conter informações pessoais ou privadas."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"ler arquivos de registro do sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, porém esses arquivos não devem conter informações pessoais ou privadas."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"ler/gravar em recursos pertencentes ao diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite que um aplicativo leia e grave em qualquer recurso que pertença ao grupo de diagnósticos; por exemplo, arquivos em /dev. Isso possivelmente pode afetar a estabilidade e a segurança do sistema. Isso deve ser usado APENAS para diagnósticos específicos do hardware realizados pelo fabricante ou pelo operador."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"ativar ou desativar os componentes do aplicativo"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Comercial"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Outros"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"por meio de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Digite o código PIN"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que o aplicativo leia todos os URLs visitados pelo Navegador e todos os favoritos do Navegador."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e favoritos do Navegador"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que um aplicativo modifique o histórico ou os favoritos do Navegador armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar os dados do seu Navegador."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modifique as permissões de geolocalização do seu navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite que um aplicativo modifique as permissões de geolocalização do navegador. Aplicativos maliciosos podem usar isso para permitir o envio de informações de localização a sites arbitrários."</string>
     <string name="save_password_message" msgid="767344687139195790">"Deseja que o navegador lembre desta senha?"</string>
@@ -642,7 +654,7 @@
   </plurals>
   <plurals name="abbrev_num_seconds_ago">
     <item quantity="one" msgid="1849036840200069118">"1 seg. atrás"</item>
-    <item quantity="other" msgid="3699169366650930415">"<xliff:g id="COUNT">%d</xliff:g> segundos\n  atrás"</item>
+    <item quantity="other" msgid="3699169366650930415">"<xliff:g id="COUNT">%d</xliff:g> segundos   atrás"</item>
   </plurals>
   <plurals name="abbrev_num_minutes_ago">
     <item quantity="one" msgid="6361490147113871545">"1 minuto atrás"</item>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Selecionar tudo"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Selecionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Selecionar palavra"</string>
     <string name="cut" msgid="3092569408438626261">"Recortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 29b7e46..748474f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Позволяет приложению освобождать память телефона с помощью удаления файлов из каталога кэша приложений. Обычно это разрешается только системным процессам."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Перемещать ресурсы приложения"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Позволяет приложению перемещать ресурсы приложения с внутренних на внешние носители и наоборот."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"считывать системные файлы журналов"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Позволяет приложению считывать информацию из различных журналов системы. Приложение может получить сведения о работе пользователя с телефоном, но они не должны содержать какой-либо личной или конфиденциальной информации."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"считывать системные файлы журналов"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Позволяет приложению считывать информацию из различных журналов системы. Приложение может получить сведения о работе пользователя с телефоном, но они не должны содержать какой-либо личной или конфиденциальной информации."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"считывать/записывать данные в ресурсы, принадлежащие группе диагностики"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Позволяет приложению считывать и записывать данные в любые ресурсы, принадлежащие группе диагностики (например, файлы в каталоге /dev). Это может повлиять на стабильность и безопасность системы. Эта возможность может быть использована ТОЛЬКО производителем или оператором для диагностики аппаратного обеспечения."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"включать или отключать компоненты приложения"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Работа"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Другое"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Создать свой ярлык"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"с помощью <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> с помощью <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Введите PIN-код"</string>
@@ -555,7 +563,7 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-карта заблокирована."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Разблокировка SIM-карты…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. После <xliff:g id="NUMBER_1">%d</xliff:g> неудачных попыток вам будет предложено разблокировать телефон с помощью учетных данных Google.\n "\n\n" Повторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. После <xliff:g id="NUMBER_1">%d</xliff:g> неудачных попыток вам будет предложено разблокировать телефон с помощью учетных данных Google.  "\n\n" Повторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> с."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Забыли графический ключ?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Снятие блокировки аккаунта"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Разрешает приложению считывать все URL, посещенные браузером, и все его закладки."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"записывать историю и закладки браузера"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Разрешает приложению изменять историю и закладки браузера, сохраненные в вашем телефоне. Вредоносное ПО может пользоваться этим, чтобы стирать или изменять данные вашего браузера."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Изменить разрешения браузера для доступа к географическому местоположению"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Позволяет программе изменять разрешения браузера для доступа к географическому положению. Вредоносные программы могут пользоваться этим для отправки информации о местоположении на некоторые сайты."</string>
     <string name="save_password_message" msgid="767344687139195790">"Вы хотите, чтобы браузер запомнил этот пароль?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Выбрать все"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Выбрать текст"</string>
+    <string name="selectText" msgid="4862359311088898878">"Выберите слово"</string>
     <string name="cut" msgid="3092569408438626261">"Вырезать"</string>
     <string name="copy" msgid="2681946229533511987">"Копировать"</string>
     <string name="paste" msgid="5629880836805036433">"Вставить"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index b53f219..469901a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillåter att ett program frigör lagringsutrymme i telefonen genom att ta bort filer i programmets katalog för cachelagring. Åtkomst är mycket begränsad, vanligtvis till systemprocesser."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Flytta programresurser"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillåter att ett program flyttar programresurser från interna till externa medier och tvärt om."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"läsa systemets loggfiler"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Tillåter att ett program läser från systemets olika loggfiler. Det innebär att programmet kan upptäcka allmän information om vad du gör med telefonen, men den bör inte innehålla personlig eller privat information."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"läsa systemets loggfiler"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Tillåter att ett program läser från systemets olika loggfiler. Det innebär att programmet kan upptäcka allmän information om vad du gör med telefonen, men den bör inte innehålla personlig eller privat information."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"läsa/skriva till resurser som ägs av diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Tillåter att ett program läser och skriver till en resurs som ägs av diag-gruppen; till exempel filer i /dev. Detta kan eventuellt påverka systemets stabilitet och säkerhet. Detta bör ENDAST används av tillverkaren eller operatören för maskinvaruspecifik diagnostik."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"aktivera eller inaktivera programkomponenter"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Arbete"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Övrigt"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Anpassad"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ange PIN-kod"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillåter att program läser alla webbadresser som webbläsaren har öppnat och alla webbläsarens bokmärken."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriva webbläsarhistorik och bokmärken"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Tillåter att ett program ändrar webbläsarhistoriken och bokmärkena i din telefon. Skadliga program kan använda detta för att ta bort eller ändra data i webbläsaren."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Ändra geografisk plats för webbläsaren"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Tillåter att ett program ändrar webbläsarens behörigheter för geografisk plats. Skadliga program kan använda detta för att tillåta att platsinformation skickas till godtyckliga webbplatser."</string>
     <string name="save_password_message" msgid="767344687139195790">"Vill du att webbläsaren ska komma ihåg lösenordet?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Välj alla"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Markera text"</string>
+    <string name="selectText" msgid="4862359311088898878">"Välj ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiera"</string>
     <string name="paste" msgid="5629880836805036433">"Klistra in"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index ea46db0..4ce0989 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Uygulamaların uygulama önbelleği dizinindeki dosyaları silerek telefonda yer açmasına izin verir. Erişim genellikle sistem işlemlerine ve yüksek düzeyde kısıtlı olarak verilir."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Uygulama kaynaklarını taşı"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Bir uygulamanın, uygulama kaynaklarını dahili ve harici ortamlar arasında taşımasına olanak tanır."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"sistem günlük dosyalarını oku"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Uygulamaların sistemin çeşitli günlük dosyalarından okumalarına izin verir. Bu, uygulamaların telefon ile neler yaptığınız ile ilgili genel bilgi bulmasına izin verir, ancak bunlar kişisel veya özel bir bilgi içermemelidir."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"sistem günlük dosyalarını oku"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Uygulamaların sistemin çeşitli günlük dosyalarından okumalarına izin verir. Bu, uygulamaların telefon ile neler yaptığınız ile ilgili genel bilgi bulmasına izin verir, ancak bunlar kişisel veya özel bir bilgi içermemelidir."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"sahibi tanılama olan kaynakları oku/bunlara yaz"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Uygulamanın tanılama grubundaki bir kaynağa ait herhangi bir kaynağı; örneğin /dev içindeki dosyaları okumasına ve bunlara yazmasına izin verir. Bu işlevin sistem kararlılığını ve güvenliğini olumsuz etkileme olasılığı vardır. Üretici veya operatör tarafından YALNIZCA donanıma özgü tanılama için kullanılmalıdır."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"uygulama bileşenlerini etkinleştir veya devre dışı bırak"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"İş"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Diğer"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Özel"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g> aracılığıyla"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="SOURCE">%2$s</xliff:g> ile <xliff:g id="DATE">%1$s</xliff:g> tarihinde"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN kodunu gir"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Uygulamaya Tarayıcının ziyaret etmiş olduğu tüm URL\'leri ve Tarayıcının tüm favorilerini okuma izni verir."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Tarayıcı geçmişini ve favorileri yaz"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Uygulamaya telefonunuzda depolanan Tarayıcı geçmişini veya favorileri değiştirme izni verir. Kötü amaçlı uygulamalar bunu Tarayıcı verilerinizi silmek veya değiştirmek için kullanabilir."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Tarayıcı\'nın coğrafi konum izinlerini değiştir"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Bir uygulamanın, Tarayıcı\'nın coğrafi konum izinlerini değiştirmesine izin verir. Kötü amaçlı uygulamalar, bu özelliği konum bilgilerini rastgele web sitelerine göndermek için kullanabilir."</string>
     <string name="save_password_message" msgid="767344687139195790">"Tarayıcının bu şifreyi anımsamasını istiyor musunuz?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Tümünü seç"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Metin seç"</string>
+    <string name="selectText" msgid="4862359311088898878">"Kelime seçin"</string>
     <string name="cut" msgid="3092569408438626261">"Kes"</string>
     <string name="copy" msgid="2681946229533511987">"Kopyala"</string>
     <string name="paste" msgid="5629880836805036433">"Yapıştır"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b963612..02ac746 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允许应用程序通过删除应用程序缓存目录中的文件释放手机存储空间。通常此权限只适用于系统进程。"</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"移动应用程序资源"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"允许应用程序在内部介质和外部介质之间移动应用程序资源。"</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"读取系统日志文件"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"允许应用程序从系统的各日志文件中读取信息。这样应用程序可以发现您的手机使用情况,但这些信息不应包含任何个人信息或保密信息。"</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"读取系统日志文件"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"允许应用程序从系统的各日志文件中读取信息。这样应用程序可以发现您的手机使用情况,但这些信息不应包含任何个人信息或保密信息。"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"读取/写入诊断所拥有的资源"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"允许应用程序读取/写入诊断组所拥有的任何资源(例如,/dev 中的文件)。这可能会影响系统稳定性和安全性。此权限仅供制造商或运营商诊断硬件问题。"</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"启用或停用应用程序组件"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"公司"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"其他"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"自定义"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"通过 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"时间:<xliff:g id="DATE">%1$s</xliff:g>,方式:<xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"输入 PIN 码"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允许应用程序读取用浏览器访问过的所有网址,以及浏览器的所有书签。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"写入浏览器的历史记录和书签"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"允许应用程序修改存储在手机中的浏览器历史记录或书签。恶意应用程序可借此清除或修改浏览器数据。"</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"修改浏览器的地理位置权限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"允许应用程序修改浏览器的地理位置权限。恶意应用程序会利用这一点将位置信息发送到任意网站。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否希望浏览器记住此密码?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"全选"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"选择文字"</string>
+    <string name="selectText" msgid="4862359311088898878">"选择文字"</string>
     <string name="cut" msgid="3092569408438626261">"剪切"</string>
     <string name="copy" msgid="2681946229533511987">"复制"</string>
     <string name="paste" msgid="5629880836805036433">"粘贴"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 939510c..434c2e0 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允許應用程式刪除快取目錄裡的檔案,釋放儲存空間。此操作通常受到系統程序嚴格限制。"</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"移動應用程式資源"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"允許應用程式將應用程式資源從內部媒體移到外部媒體,反之亦可。"</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"讀取系統記錄檔"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"允許應用程式讀取系統記錄檔。此項操作可讓應用程式了解目前手機操作狀態,但內容應不含任何個人或隱私資訊。"</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"讀取系統記錄檔"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"允許應用程式讀取系統記錄檔。此項操作可讓應用程式了解目前手機操作狀態,但內容應不含任何個人或隱私資訊。"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"讀寫 diag 擁有的資源"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"允許應用程式讀寫 diag 群組的資源;例如:/dev 裡的檔案。這可能會影響系統穩定性與安全性。此功能僅供製造商或技術人員用於硬體規格偵測。"</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"啟用或停用應用程式元件"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"公司"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"其他"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"自訂"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"透過 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>透過「<xliff:g id="SOURCE">%2$s</xliff:g>」"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
@@ -587,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允許應用程式讀取瀏覽器曾經造訪過的所有網址,以及瀏覽器的所有書籤。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"寫入瀏覽器的記錄與書籤"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"允許應用程式修改儲存在電話上的瀏覽記錄或書籤。請注意:惡意應用程式可能會使用此選項來清除或修改您瀏覽器的資料。"</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"修改瀏覽器地理資訊的權限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"允許應用程式修改瀏覽器的地理位置權限,惡意應用程式可能會透過此方式允許將您的位置資訊任意傳送給某些網站。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否記住此密碼?"</string>
@@ -699,7 +711,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"全部選取"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"選取文字"</string>
+    <string name="selectText" msgid="4862359311088898878">"選取字詞"</string>
     <string name="cut" msgid="3092569408438626261">"剪下"</string>
     <string name="copy" msgid="2681946229533511987">"複製"</string>
     <string name="paste" msgid="5629880836805036433">"貼上"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ac1c0dd..7b0a502 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -424,6 +424,9 @@
         <attr name="quickContactBadgeStyleSmallWindowMedium" format="reference" />
         <!-- Default quickcontact badge style with large quickcontact window. -->
         <attr name="quickContactBadgeStyleSmallWindowLarge" format="reference" />
+        <!-- Reference to a style that will be used for the window containing a text
+             selection anchor. -->
+        <attr name="textSelectHandleWindowStyle" format="reference" />
 
         <!-- =================== -->
         <!-- Preference styles   -->
@@ -2236,6 +2239,7 @@
     </declare-styleable>
     <declare-styleable name="PopupWindow">
         <attr name="popupBackground" format="reference|color" />
+        <attr name="windowAnimationStyle" />
     </declare-styleable>
     <declare-styleable name="ViewAnimator">
         <attr name="inAnimation" format="reference" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index fb49120..a8c00a6 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -75,7 +75,8 @@
          header in the Action Bar. The primary differences between an icon
          and a logo are that logos are often wider and more detailed, and are
          used without an accompanying text caption. This must be a reference
-         to a Drawable resource containing the image definition. -->
+         to a Drawable resource containing the image definition.
+         @hide -->
     <attr name="logo" format="reference" />
 
     <!-- Name of the activity to be launched to manage application's space on
@@ -399,7 +400,8 @@
     <attr name="syncable" format="boolean" />
     
     <!-- Flag declaring this activity to be 'immersive'; immersive activities
-         should not be interrupted with other activities or notifications. -->
+         should not be interrupted with other activities or notifications.
+         @hide -->
     <attr name="immersive" format="boolean" />
 
     <!-- Specify the order in which content providers hosted by a process
@@ -715,7 +717,6 @@
         <attr name="theme" />
         <attr name="label" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="description" />
         <attr name="permission" />
         <attr name="process" />
@@ -774,7 +775,6 @@
         <attr name="name" />
         <attr name="label" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="permissionGroup" />
         <attr name="description" />
         <attr name="protectionLevel" />
@@ -799,7 +799,6 @@
         <attr name="name" />
         <attr name="label" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="description" />
     </declare-styleable>
     
@@ -829,7 +828,6 @@
         <attr name="name" />
         <attr name="label" />
         <attr name="icon" />
-        <attr name="logo" />
     </declare-styleable>
     
     <!-- The <code>uses-permission</code> tag requests a
@@ -1034,7 +1032,6 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="process" />
         <attr name="authorities" />
         <attr name="syncable" />
@@ -1114,7 +1111,6 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="permission" />
         <attr name="process" />
         <!-- Specify whether the service is enabled or not (that is, can be instantiated by the system).
@@ -1147,7 +1143,6 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="permission" />
         <attr name="process" />
         <!-- Specify whether the receiver is enabled or not (that is, can be instantiated by the system).
@@ -1180,7 +1175,6 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="launchMode" />
         <attr name="screenOrientation" />
         <attr name="configChanges" />
@@ -1206,7 +1200,6 @@
              this activity.  A value besides "unspecified" here overrides
              any value in the theme. -->
         <attr name="windowSoftInputMode" />
-        <attr name="immersive" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
@@ -1235,7 +1228,6 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="permission" />
         <!-- Specify whether the activity-alias is enabled or not (that is, can be instantiated by the system).
              It can also be specified for an application as a whole, in which case a value of "false"
@@ -1305,7 +1297,6 @@
          parent="AndroidManifestActivity AndroidManifestReceiver AndroidManifestService">
         <attr name="label" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="priority" />
     </declare-styleable>
     
@@ -1412,7 +1403,6 @@
         <attr name="targetPackage" />
         <attr name="label" />
         <attr name="icon" />
-        <attr name="logo" />
         <attr name="handleProfiling" />
         <attr name="functionalTest" />
     </declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 720dc97..d120dd0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -36,7 +36,6 @@
        <item><xliff:g id="id">speakerphone</xliff:g></item>
        <item><xliff:g id="id">mute</xliff:g></item>
        <item><xliff:g id="id">volume</xliff:g></item>
-       <item><xliff:g id="id">tty</xliff:g></item>
        <item><xliff:g id="id">wifi</xliff:g></item>
        <item><xliff:g id="id">cdma_eri</xliff:g></item>
        <item><xliff:g id="id">data_connection</xliff:g></item>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2c1e91d..3fc93e7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1258,6 +1258,7 @@
   <public type="attr" name="textSelectHandleLeft" id="0x010102c5" />
   <public type="attr" name="textSelectHandleRight" id="0x010102c6" />
   <public type="attr" name="textSelectHandle" id="0x010102c7" />
+  <public type="attr" name="textSelectHandleWindowStyle" id="0x010102c8" />
 
   <public-padding type="attr" name="kraken_resource_pad" end="0x01010300" />
   
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 808b371..ebccfb6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1619,6 +1619,15 @@
 
     <!-- Title of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
+    <string name="permlab_setAlarm">set alarm in alarm clock</string>
+    <!-- Description of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. -->
+    <string name="permdesc_setAlarm">Allows the application to set an alarm in
+      an installed alarm clock application. Some alarm clock applications may
+      not implement this feature.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. -->
     <string name="permlab_writeGeolocationPermissions">Modify Browser geolocation permissions</string>
     <!-- Description of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index e93b570..4cdf4f6f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -202,10 +202,12 @@
         <item name="android:textStyle">bold</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent">
+        <item name="android:textColor">#ff6b6b6b</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent.Title">
         <item name="android:textSize">16sp</item>
         <item name="android:textStyle">bold</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
     </style>
 
     <!-- Widget Styles -->
@@ -857,4 +859,15 @@
         <item name="android:paddingBottom">1dip</item>
         <item name="android:background">@android:drawable/bottom_bar</item>
     </style>
+
+    <!-- Style for the small popup windows that contain text selection anchors. -->
+    <style name="Widget.TextSelectHandle">
+        <item name="android:windowAnimationStyle">@android:style/Animation.TextSelectHandle</item>
+    </style>
+
+    <!-- Style for animating text selection handles. -->
+    <style name="Animation.TextSelectHandle">
+        <item name="windowEnterAnimation">@android:anim/fade_in</item>
+        <item name="windowExitAnimation">@android:anim/fade_out</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 7d6ca06..e7cb593 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -140,6 +140,7 @@
         <item name="textSelectHandleLeft">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
+        <item name="textSelectHandleWindowStyle">@android:style/Widget.TextSelectHandle</item>
 
         <!-- Widget styles -->
         <item name="absListViewStyle">@android:style/Widget.AbsListView</item>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f09421b..487a00d 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -35,6 +35,7 @@
             android:label="@string/permlab_testDenied"
             android:description="@string/permdesc_testDenied" />
 
+    <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
diff --git a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
similarity index 96%
rename from core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index ee0f5f1..ed42e64 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -14,19 +14,20 @@
  * 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;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
@@ -43,9 +44,12 @@
 import java.io.IOException;
 import java.net.URL;
 import java.util.concurrent.TimeoutException;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 import java.util.Vector;
 
 import junit.framework.AssertionFailedError;
@@ -67,6 +71,7 @@
 
     protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
     protected static final int HTTP_OK = 200;
+    protected static final int HTTP_REDIRECT = 307;
     protected static final int HTTP_PARTIAL_CONTENT = 206;
     protected static final int HTTP_NOT_FOUND = 404;
     protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
@@ -119,6 +124,7 @@
 
     public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
         private volatile int mNumDownloadsCompleted = 0;
+        private Set<Long> downloadIds = Collections.synchronizedSet(new HashSet<Long>());
 
         /**
          * {@inheritDoc}
@@ -129,6 +135,8 @@
                 ++mNumDownloadsCompleted;
                 Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
                         intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
+                Bundle extras = intent.getExtras();
+                downloadIds.add(new Long(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID)));
             }
         }
 
@@ -142,6 +150,18 @@
         public int numDownloadsCompleted() {
             return mNumDownloadsCompleted;
         }
+
+        /**
+         * Gets the list of download IDs.
+         * @return A Set<Long> with the ids of the completed downloads.
+         */
+        public Set<Long> getDownloadIds() {
+            synchronized(downloadIds) {
+                Set<Long> returnIds = new HashSet<Long>(downloadIds);
+                return returnIds;
+            }
+        }
+
     }
 
     public static class WiFiChangedReceiver extends BroadcastReceiver {
@@ -196,6 +216,17 @@
     }
 
     /**
+     * Helper to enqueue a response from the MockWebServer with no body.
+     *
+     * @param status The HTTP status code to return for this response
+     * @return Returns the mock web server response that was queued (which can be modified)
+     */
+    protected MockResponse enqueueResponse(int status) {
+        return doEnqueueResponse(status);
+
+    }
+
+    /**
      * Helper to enqueue a response from the MockWebServer.
      *
      * @param status The HTTP status code to return for this response
diff --git a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
new file mode 100644
index 0000000..38f336e
--- /dev/null
+++ b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
@@ -0,0 +1,589 @@
+/*
+ * 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.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.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Random;
+import java.util.Set;
+
+import junit.framework.AssertionFailedError;
+
+import coretestutils.http.MockResponse;
+import coretestutils.http.MockWebServer;
+
+/**
+ * Integration tests of the DownloadManager API.
+ */
+public class DownloadManagerIntegrationTest extends DownloadManagerBaseTest {
+
+    private final static String LOG_TAG = "android.net.DownloadManagerIntegrationTest";
+    private final static String PROHIBITED_DIRECTORY =
+            Environment.getRootDirectory().getAbsolutePath();
+    private final static String CACHE_DIR =
+            Environment.getDownloadCacheDirectory().getAbsolutePath();
+    protected MultipleDownloadsCompletedReceiver mReceiver = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setWiFiStateOn(true);
+        mServer.play();
+        removeAllCurrentDownloads();
+        mReceiver = registerNewMultipleDownloadsReceiver();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        setWiFiStateOn(true);
+        removeAllCurrentDownloads();
+
+        if (mReceiver != null) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    /**
+     * Helper that does the actual basic download verification.
+     */
+    protected long doBasicDownload(byte[] blobData) throws Exception {
+        long dlRequest = doStandardEnqueue(blobData);
+
+        // wait for the download to complete
+        waitForDownloadOrTimeout(dlRequest);
+
+        assertEquals(1, mReceiver.numDownloadsCompleted());
+        return dlRequest;
+    }
+
+    /**
+     * Verifies a particular error code was received from a download
+     *
+     * @param uri The uri to enqueue to the DownloadManager
+     * @param error The error code expected
+     * @throws an Exception if the test fails
+     */
+    @LargeTest
+    public void doErrorTest(Uri uri, int error) throws Exception {
+        Request request = new Request(uri);
+        request.setTitle(DEFAULT_FILENAME);
+
+        long dlRequest = mDownloadManager.enqueue(request);
+        waitForDownloadOrTimeout(dlRequest);
+
+        Cursor cursor = getCursor(dlRequest);
+        try {
+            verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE, error);
+        } finally {
+            cursor.close();
+        }
+    }
+
+    /**
+     * Test a basic download of a binary file 500k in size.
+     */
+    @LargeTest
+    public void testBasicBinaryDownload() throws Exception {
+        int fileSize = 500 * 1024;  // 500k
+        byte[] blobData = generateData(fileSize, DataType.BINARY);
+
+        long dlRequest = doBasicDownload(blobData);
+        verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+    }
+
+    /**
+     * Tests the basic downloading of a text file 300000 bytes in size.
+     */
+    @LargeTest
+    public void testBasicTextDownload() throws Exception {
+        int fileSize = 300000;
+        byte[] blobData = generateData(fileSize, DataType.TEXT);
+
+        long dlRequest = doBasicDownload(blobData);
+        verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+    }
+
+    /**
+     * Tests when the server drops the connection after all headers (but before any data send).
+     */
+    @LargeTest
+    public void testDropConnection_headers() throws Exception {
+        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
+
+        MockResponse response = enqueueResponse(HTTP_OK, blobData);
+        response.setCloseConnectionAfterHeader("content-length");
+        long dlRequest = doCommonStandardEnqueue();
+
+        // Download will never complete when header is dropped
+        boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
+                DEFAULT_MAX_WAIT_TIME);
+
+        assertFalse(success);
+    }
+
+    /**
+     * Tests that we get an error code when the server drops the connection during a download.
+     */
+    @LargeTest
+    public void testServerDropConnection_body() throws Exception {
+        byte[] blobData = generateData(25000, DataType.TEXT);  // file size = 25000 bytes
+
+        MockResponse response = enqueueResponse(HTTP_OK, blobData);
+        response.setCloseConnectionAfterXBytes(15382);
+        long dlRequest = doCommonStandardEnqueue();
+        waitForDownloadOrTimeout(dlRequest);
+
+        Cursor cursor = getCursor(dlRequest);
+        try {
+            verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
+            verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
+                    DownloadManager.ERROR_CANNOT_RESUME);
+        } finally {
+            cursor.close();
+        }
+        // Even tho the server drops the connection, we should still get a completed notification
+        assertEquals(1, mReceiver.numDownloadsCompleted());
+    }
+
+    /**
+     * Attempts to download several files simultaneously
+     */
+    @LargeTest
+    public void testMultipleDownloads() throws Exception {
+        // need to be sure all current downloads have stopped first
+        removeAllCurrentDownloads();
+        int NUM_FILES = 50;
+        int MAX_FILE_SIZE = 500 * 1024; // 500 kb
+
+        Random r = new LoggingRng();
+        for (int i=0; i<NUM_FILES; ++i) {
+            int size = r.nextInt(MAX_FILE_SIZE);
+            byte[] blobData = generateData(size, DataType.TEXT);
+
+            Uri uri = getServerUri(DEFAULT_FILENAME);
+            Request request = new Request(uri);
+            request.setTitle(String.format("%s--%d", DEFAULT_FILENAME, i));
+
+            // Prepare the mock server with a standard response
+            enqueueResponse(HTTP_OK, blobData);
+
+            Log.i(LOG_TAG, "request: " + i);
+            mDownloadManager.enqueue(request);
+        }
+
+        waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
+        Cursor cursor = mDownloadManager.query(new Query());
+        try {
+            assertEquals(NUM_FILES, cursor.getCount());
+
+            if (cursor.moveToFirst()) {
+                do {
+                    int status = cursor.getInt(cursor.getColumnIndex(
+                            DownloadManager.COLUMN_STATUS));
+                    String filename = cursor.getString(cursor.getColumnIndex(
+                            DownloadManager.COLUMN_URI));
+                    String errorString = String.format(
+                            "File %s failed to download successfully. Status code: %d",
+                            filename, status);
+                    assertEquals(errorString, DownloadManager.STATUS_SUCCESSFUL, status);
+                } while (cursor.moveToNext());
+            }
+
+            assertEquals(NUM_FILES, mReceiver.numDownloadsCompleted());
+        } finally {
+            cursor.close();
+        }
+    }
+
+    /**
+     * Tests trying to download to SD card when the file with same name already exists.
+     */
+    @LargeTest
+    public void testDownloadToExternal_fileExists() throws Exception {
+        File existentFile = createFileOnSD(null, 1, DataType.TEXT, null);
+        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
+
+        // Prepare the mock server with a standard response
+        enqueueResponse(HTTP_OK, blobData);
+
+        try {
+            Uri uri = getServerUri(DEFAULT_FILENAME);
+            Request request = new Request(uri);
+
+            Uri localUri = Uri.fromFile(existentFile);
+            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
+            request.setDestinationUri(localUri);
+
+            long dlRequest = mDownloadManager.enqueue(request);
+
+            // wait for the download to complete
+            waitForDownloadOrTimeout(dlRequest);
+            Cursor cursor = getCursor(dlRequest);
+
+            try {
+                verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
+                verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
+                        DownloadManager.ERROR_FILE_ERROR);
+            } finally {
+                cursor.close();
+            }
+        } finally {
+            existentFile.delete();
+        }
+    }
+
+    /**
+     * Tests trying to download a file to SD card.
+     */
+    @LargeTest
+    public void testDownloadToExternal() throws Exception {
+        String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
+        File downloadedFile = new File(localDownloadDirectory, DEFAULT_FILENAME);
+        // make sure the file doesn't already exist in the directory
+        downloadedFile.delete();
+
+        try {
+            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
+
+            // Prepare the mock server with a standard response
+            enqueueResponse(HTTP_OK, blobData);
+
+            Uri uri = getServerUri(DEFAULT_FILENAME);
+            Request request = new Request(uri);
+
+            Uri localUri = Uri.fromFile(downloadedFile);
+            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
+            request.setDestinationUri(localUri);
+
+            long dlRequest = mDownloadManager.enqueue(request);
+
+            // wait for the download to complete
+            waitForDownloadOrTimeout(dlRequest);
+
+            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+
+            assertEquals(1, mReceiver.numDownloadsCompleted());
+        } finally {
+            downloadedFile.delete();
+        }
+    }
+
+    /**
+     * Tests trying to download a file to the system partition.
+     */
+    @LargeTest
+    public void testDownloadToProhibitedDirectory() throws Exception {
+        File downloadedFile = new File(PROHIBITED_DIRECTORY, DEFAULT_FILENAME);
+        try {
+            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
+
+            // Prepare the mock server with a standard response
+            enqueueResponse(HTTP_OK, blobData);
+
+            Uri uri = getServerUri(DEFAULT_FILENAME);
+            Request request = new Request(uri);
+
+            Uri localUri = Uri.fromFile(downloadedFile);
+            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
+            request.setDestinationUri(localUri);
+
+            try {
+                mDownloadManager.enqueue(request);
+                fail("Failed to throw SecurityException when trying to write to /system.");
+            } catch (SecurityException s) {
+                assertFalse(downloadedFile.exists());
+            }
+        } finally {
+            // Just in case file somehow got created, make sure to delete it
+            downloadedFile.delete();
+        }
+    }
+
+    /**
+     * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
+     * once Wifi is re-enabled.
+     */
+    @LargeTest
+    public void testDownloadNoWifi() throws Exception {
+        long timeout = 60 * 1000; // wait only 60 seconds before giving up
+        int fileSize = 140 * 1024;  // 140k
+        byte[] blobData = generateData(fileSize, DataType.TEXT);
+
+        setWiFiStateOn(false);
+        enqueueResponse(HTTP_OK, blobData);
+
+        try {
+            Uri uri = getServerUri(DEFAULT_FILENAME);
+            Request request = new Request(uri);
+            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
+
+            long dlRequest = mDownloadManager.enqueue(request);
+
+            // wait for the download to complete
+            boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
+                    WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
+            assertFalse("Download proceeded without Wifi connection!", success);
+
+            setWiFiStateOn(true);
+            waitForDownloadOrTimeout(dlRequest);
+
+            assertEquals(1, mReceiver.numDownloadsCompleted());
+        } finally {
+            setWiFiStateOn(true);
+        }
+    }
+
+    /**
+     * Tests downloading a file to cache when there isn't enough space in the cache to hold the
+     * entire file.
+     */
+    @LargeTest
+    public void testDownloadToCache_whenFull() throws Exception {
+        int DOWNLOAD_FILE_SIZE = 500000;
+
+        StatFs fs = new StatFs(CACHE_DIR);
+        Log.i(LOG_TAG, "getAvailableBlocks: " + fs.getAvailableBlocks());
+        Log.i(LOG_TAG, "getBlockSize: " + fs.getBlockSize());
+
+        int blockSize = fs.getBlockSize();
+        int availableBlocks = fs.getAvailableBlocks();
+        int availableBytes = blockSize * availableBlocks;
+        File outFile = null;
+
+        try {
+            // fill cache to ensure we don't have enough space - take half the size of the
+            // download size, and leave that much freespace left on the cache partition
+            if (DOWNLOAD_FILE_SIZE <= availableBytes) {
+                int writeSizeBytes = availableBytes - (DOWNLOAD_FILE_SIZE / 2);
+
+                int writeSizeBlocks = writeSizeBytes / blockSize;
+                int remainderSizeBlocks = availableBlocks - writeSizeBlocks;
+
+                FileOutputStream fo = null;
+                try {
+                    outFile = File.createTempFile("DM_TEST", null, new File(CACHE_DIR));
+                    Log.v(LOG_TAG, "writing " + writeSizeBlocks + " blocks to file "
+                            + outFile.getAbsolutePath());
+
+                    fo = new FileOutputStream(outFile);
+
+                    byte[] buffer = new byte[blockSize];
+                    while (fs.getAvailableBlocks() >= remainderSizeBlocks) {
+                        fo.write(buffer);
+                        fs.restat(CACHE_DIR);
+                    }
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "error filling file: ", e);
+                    throw e;
+                } finally {
+                    if (fo != null) {
+                        fo.close();
+                    }
+                }
+            }
+
+            assertTrue(DOWNLOAD_FILE_SIZE > (fs.getAvailableBlocks() * blockSize));
+            byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
+            long dlRequest = doBasicDownload(blobData);
+            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+
+        } finally {
+            if (outFile != null) {
+                outFile.delete();
+            }
+        }
+    }
+
+    /**
+     * Tests that files are not deleted when DOWNLOAD_CACHE_NON_PURGEABLE is set, even if we've
+     * run out of space.
+     */
+    @LargeTest
+    public void testDownloadCacheNonPurgeable() throws Exception {
+        int fileSize = 10000000;
+        byte[] blobData = generateData(fileSize, DataType.BINARY);
+        long dlRequest = -1;
+
+        // Fill up the cache partition until there's not enough room for another download.
+        // Note that we need to initiate a download first, then check for the available space. This
+        // is b/c there could be some files that are still left in the cache that can (and will be)
+        // cleared out, but not until DM gets a request for a download and reclaims that
+        // space first.
+        boolean spaceAvailable = true;
+        while (spaceAvailable) {
+            dlRequest = doStandardEnqueue(blobData);
+            waitForDownloadOrTimeout(dlRequest);
+
+            // Check if we've filled up the cache yet
+            StatFs fs = new StatFs(CACHE_DIR);
+            Log.i(LOG_TAG, "getAvailableBlocks: " + fs.getAvailableBlocks());
+            Log.i(LOG_TAG, "getBlockSize: " + fs.getBlockSize());
+            int availableBytes = fs.getBlockSize() * fs.getAvailableBlocks();
+            spaceAvailable = (availableBytes > fileSize) ? true : false;
+        }
+
+        // Now add one more download (should not fit in the space left over)
+        dlRequest = doStandardEnqueue(blobData);
+        waitForDownloadOrTimeout(dlRequest);
+
+        // For the last download we should have failed b/c there is not enough space left in cache
+        Cursor cursor = getCursor(dlRequest);
+        try {
+            verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
+                    DownloadManager.ERROR_INSUFFICIENT_SPACE);
+        } finally {
+            cursor.close();
+        }
+    }
+
+    /**
+     * Tests that we get the correct download ID from the download notification.
+     */
+    @LargeTest
+    public void testGetDownloadIdOnNotification() throws Exception {
+        byte[] blobData = generateData(3000, DataType.TEXT);  // file size = 3000 bytes
+
+        MockResponse response = enqueueResponse(HTTP_OK, blobData);
+        long dlRequest = doCommonStandardEnqueue();
+        waitForDownloadOrTimeout(dlRequest);
+
+        Set<Long> ids = mReceiver.getDownloadIds();
+        assertEquals(1, ids.size());
+        Iterator<Long> it = ids.iterator();
+        assertEquals("Download ID received from notification does not match initial id!",
+                dlRequest, it.next().longValue());
+    }
+
+    /**
+     * Tests the download failure error after too many redirects (>5).
+     */
+    @LargeTest
+    public void testErrorTooManyRedirects() throws Exception {
+        Uri uri = getServerUri(DEFAULT_FILENAME);
+
+        // force 6 redirects
+        for (int i = 0; i < 6; ++i) {
+            MockResponse response = enqueueResponse(HTTP_REDIRECT);
+            response.addHeader("Location", uri.toString());
+        }
+        doErrorTest(uri, DownloadManager.ERROR_TOO_MANY_REDIRECTS);
+    }
+
+    /**
+     * Tests the download failure error from an unhandled HTTP status code
+     */
+    @LargeTest
+    public void testErrorUnhandledHttpCode() throws Exception {
+        Uri uri = getServerUri(DEFAULT_FILENAME);
+        MockResponse response = enqueueResponse(HTTP_PARTIAL_CONTENT);
+
+        doErrorTest(uri, DownloadManager.ERROR_UNHANDLED_HTTP_CODE);
+    }
+
+    /**
+     * Tests the download failure error from an unhandled HTTP status code
+     */
+    @LargeTest
+    public void testErrorHttpDataError_invalidRedirect() throws Exception {
+        Uri uri = getServerUri(DEFAULT_FILENAME);
+        MockResponse response = enqueueResponse(HTTP_REDIRECT);
+        response.addHeader("Location", "://blah.blah.blah.com");
+
+        doErrorTest(uri, DownloadManager.ERROR_HTTP_DATA_ERROR);
+    }
+
+    /**
+     * Tests that we can remove a download from the download manager.
+     */
+    @LargeTest
+    public void testRemoveDownload() throws Exception {
+        int fileSize = 100 * 1024;  // 100k
+        byte[] blobData = generateData(fileSize, DataType.BINARY);
+
+        long dlRequest = doBasicDownload(blobData);
+        Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
+        try {
+            assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
+            mDownloadManager.remove(dlRequest);
+            cursor.requery();
+            assertEquals("The count of downloads with this ID is not 0!", 0, cursor.getCount());
+        } finally {
+            cursor.close();
+        }
+    }
+
+    /**
+     * Tests that we can set the title of a download.
+     */
+    @LargeTest
+    public void testSetTitle() throws Exception {
+        int fileSize = 50 * 1024;  // 50k
+        byte[] blobData = generateData(fileSize, DataType.BINARY);
+        MockResponse response = enqueueResponse(HTTP_OK, blobData);
+
+        // An arbitrary unicode string title
+        final String title = "\u00a5123;\"\u0152\u017d \u054b \u0a07 \ucce0 \u6820\u03a8\u5c34" +
+                "\uf4ad\u0da9\uc0c5\uc1a8 \uf4c5 \uf4aa\u0023\'";
+
+        Uri uri = getServerUri(DEFAULT_FILENAME);
+        Request request = new Request(uri);
+        request.setTitle(title);
+
+        long dlRequest = mDownloadManager.enqueue(request);
+        waitForDownloadOrTimeout(dlRequest);
+
+        Cursor cursor = getCursor(dlRequest);
+        try {
+            verifyString(cursor, DownloadManager.COLUMN_TITLE, title);
+        } finally {
+            cursor.close();
+        }
+    }
+}
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/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 975a4c2..1289a9e 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -167,8 +167,7 @@
         return ipm;
     }
 
-    public boolean invokeInstallPackage(Uri packageURI, int flags,
-            GenericReceiver receiver) throws Exception {
+    public boolean invokeInstallPackage(Uri packageURI, int flags, GenericReceiver receiver) {
         PackageInstallObserver observer = new PackageInstallObserver();
         final boolean received = false;
         mContext.registerReceiver(receiver, receiver.filter);
@@ -180,11 +179,15 @@
                     getPm().installPackage(packageURI, observer, flags, null);
                     long waitTime = 0;
                     while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
-                        observer.wait(WAIT_TIME_INCR);
-                        waitTime += WAIT_TIME_INCR;
+                        try {
+                            observer.wait(WAIT_TIME_INCR);
+                            waitTime += WAIT_TIME_INCR;
+                        } catch (InterruptedException e) {
+                            Log.i(TAG, "Interrupted during sleep", e);
+                        }
                     }
                     if(!observer.isDone()) {
-                        throw new Exception("Timed out waiting for packageInstalled callback");
+                        fail("Timed out waiting for packageInstalled callback");
                     }
                     if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                         Log.i(TAG, "Failed to install with error code = " + observer.returnCode);
@@ -193,11 +196,15 @@
                     // Verify we received the broadcast
                     waitTime = 0;
                     while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
-                        receiver.wait(WAIT_TIME_INCR);
-                        waitTime += WAIT_TIME_INCR;
+                        try {
+                            receiver.wait(WAIT_TIME_INCR);
+                            waitTime += WAIT_TIME_INCR;
+                        } catch (InterruptedException e) {
+                            Log.i(TAG, "Interrupted during sleep", e);
+                        }
                     }
                     if(!receiver.isDone()) {
-                        throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+                        fail("Timed out waiting for PACKAGE_ADDED notification");
                     }
                     return receiver.received;
                 }
@@ -207,7 +214,7 @@
         }
     }
 
-    public void invokeInstallPackageFail(Uri packageURI, int flags, int result) throws Exception {
+    public void invokeInstallPackageFail(Uri packageURI, int flags, int result) {
         PackageInstallObserver observer = new PackageInstallObserver();
         try {
             // Wait on observer
@@ -215,11 +222,15 @@
                 getPm().installPackage(packageURI, observer, flags, null);
                 long waitTime = 0;
                 while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
-                    observer.wait(WAIT_TIME_INCR);
-                    waitTime += WAIT_TIME_INCR;
+                    try {
+                        observer.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    } catch (InterruptedException e) {
+                        Log.i(TAG, "Interrupted during sleep", e);
+                    }
                 }
                 if(!observer.isDone()) {
-                    throw new Exception("Timed out waiting for packageInstalled callback");
+                    fail("Timed out waiting for packageInstalled callback");
                 }
                 assertEquals(observer.returnCode, result);
             }
@@ -265,7 +276,7 @@
                 Environment.getExternalStorageDirectory().getPath());
         sdSize = (long)sdStats.getAvailableBlocks() *
                 (long)sdStats.getBlockSize();
-        // TODO check for thesholds here
+        // TODO check for thresholds here
         return pkgLen <= sdSize;
 
     }
@@ -368,12 +379,21 @@
                     assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
                     assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath()));
                 } else if (rLoc == INSTALL_LOC_SD){
-                    assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
-                    assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX));
-                    assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX));
-                    assertTrue(info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
+                    assertTrue("Application flags (" + info.flags
+                            + ") should contain FLAG_EXTERNAL_STORAGE",
+                            (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+                    assertTrue("The APK path (" + srcPath + ") should start with "
+                            + SECURE_CONTAINERS_PREFIX, srcPath
+                            .startsWith(SECURE_CONTAINERS_PREFIX));
+                    assertTrue("The public APK path (" + publicSrcPath + ") should start with "
+                            + SECURE_CONTAINERS_PREFIX, publicSrcPath
+                            .startsWith(SECURE_CONTAINERS_PREFIX));
+                    assertTrue("The native library path (" + info.nativeLibraryDir
+                            + ") should start with " + SECURE_CONTAINERS_PREFIX,
+                            info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
                 } else {
                     // TODO handle error. Install should have failed.
+                    fail("Install should have failed");
                 }
             }
         } catch (NameNotFoundException e) {
@@ -544,8 +564,6 @@
                 // Verify installed information
                 assertInstall(pkg, flags, expInstallLocation);
             }
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
         } finally {
             if (cleanUp) {
                 cleanUpInstall(ip);
diff --git a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
deleted file mode 100644
index be3cbf7..0000000
--- a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.Cursor;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
-import android.net.DownloadManagerBaseTest.DataType;
-import android.net.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
-import android.net.wifi.WifiManager;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Random;
-
-import junit.framework.AssertionFailedError;
-
-import coretestutils.http.MockResponse;
-import coretestutils.http.MockWebServer;
-
-/**
- * Integration tests of the DownloadManager API.
- */
-public class DownloadManagerIntegrationTest extends DownloadManagerBaseTest {
-
-    private static String LOG_TAG = "android.net.DownloadManagerIntegrationTest";
-    private static String PROHIBITED_DIRECTORY = "/system";
-    protected MultipleDownloadsCompletedReceiver mReceiver = null;
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        setWiFiStateOn(true);
-        mServer.play();
-        removeAllCurrentDownloads();
-        mReceiver = registerNewMultipleDownloadsReceiver();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-        setWiFiStateOn(true);
-
-        if (mReceiver != null) {
-            mContext.unregisterReceiver(mReceiver);
-            mReceiver = null;
-            removeAllCurrentDownloads();
-        }
-    }
-
-    /**
-     * Helper that does the actual basic download verification.
-     */
-    protected void doBasicDownload(byte[] blobData) throws Exception {
-        long dlRequest = doStandardEnqueue(blobData);
-
-        // wait for the download to complete
-        waitForDownloadOrTimeout(dlRequest);
-
-        verifyAndCleanupSingleFileDownload(dlRequest, blobData);
-        assertEquals(1, mReceiver.numDownloadsCompleted());
-    }
-
-    /**
-     * Test a basic download of a binary file 500k in size.
-     */
-    @LargeTest
-    public void testBasicBinaryDownload() throws Exception {
-        int fileSize = 500 * 1024;  // 500k
-        byte[] blobData = generateData(fileSize, DataType.BINARY);
-
-        doBasicDownload(blobData);
-    }
-
-    /**
-     * Tests the basic downloading of a text file 300000 bytes in size.
-     */
-    @LargeTest
-    public void testBasicTextDownload() throws Exception {
-        int fileSize = 300000;
-        byte[] blobData = generateData(fileSize, DataType.TEXT);
-
-        doBasicDownload(blobData);
-    }
-
-    /**
-     * Tests when the server drops the connection after all headers (but before any data send).
-     */
-    @LargeTest
-    public void testDropConnection_headers() throws Exception {
-        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
-
-        MockResponse response = enqueueResponse(HTTP_OK, blobData);
-        response.setCloseConnectionAfterHeader("content-length");
-        long dlRequest = doCommonStandardEnqueue();
-
-        // Download will never complete when header is dropped
-        boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
-                DEFAULT_MAX_WAIT_TIME);
-
-        assertFalse(success);
-    }
-
-    /**
-     * Tests that we get an error code when the server drops the connection during a download.
-     */
-    @LargeTest
-    public void testServerDropConnection_body() throws Exception {
-        byte[] blobData = generateData(25000, DataType.TEXT);  // file size = 25000 bytes
-
-        MockResponse response = enqueueResponse(HTTP_OK, blobData);
-        response.setCloseConnectionAfterXBytes(15382);
-        long dlRequest = doCommonStandardEnqueue();
-        waitForDownloadOrTimeout(dlRequest);
-
-        Cursor cursor = getCursor(dlRequest);
-        try {
-            verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
-            verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
-                    DownloadManager.ERROR_CANNOT_RESUME);
-        } finally {
-            cursor.close();
-        }
-        // Even tho the server drops the connection, we should still get a completed notification
-        assertEquals(1, mReceiver.numDownloadsCompleted());
-    }
-
-    /**
-     * Attempts to download several files simultaneously
-     */
-    @LargeTest
-    public void testMultipleDownloads() throws Exception {
-        // need to be sure all current downloads have stopped first
-        removeAllCurrentDownloads();
-        int NUM_FILES = 50;
-        int MAX_FILE_SIZE = 500 * 1024; // 500 kb
-
-        Random r = new LoggingRng();
-        for (int i=0; i<NUM_FILES; ++i) {
-            int size = r.nextInt(MAX_FILE_SIZE);
-            byte[] blobData = generateData(size, DataType.TEXT);
-
-            Uri uri = getServerUri(DEFAULT_FILENAME);
-            Request request = new Request(uri);
-            request.setTitle(String.format("%s--%d", DEFAULT_FILENAME, i));
-
-            // Prepare the mock server with a standard response
-            enqueueResponse(HTTP_OK, blobData);
-
-            Log.i(LOG_TAG, "request: " + i);
-            mDownloadManager.enqueue(request);
-        }
-
-        waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
-        Cursor cursor = mDownloadManager.query(new Query());
-        try {
-            assertEquals(NUM_FILES, cursor.getCount());
-
-            if (cursor.moveToFirst()) {
-                do {
-                    int status = cursor.getInt(cursor.getColumnIndex(
-                            DownloadManager.COLUMN_STATUS));
-                    String filename = cursor.getString(cursor.getColumnIndex(
-                            DownloadManager.COLUMN_URI));
-                    String errorString = String.format(
-                            "File %s failed to download successfully. Status code: %d",
-                            filename, status);
-                    assertEquals(errorString, DownloadManager.STATUS_SUCCESSFUL, status);
-                } while (cursor.moveToNext());
-            }
-
-            assertEquals(NUM_FILES, mReceiver.numDownloadsCompleted());
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * Tests trying to download to SD card when the file with same name already exists.
-     */
-    @LargeTest
-    public void testDownloadToExternal_fileExists() throws Exception {
-        File existentFile = createFileOnSD(null, 1, DataType.TEXT, null);
-        byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
-
-        // Prepare the mock server with a standard response
-        enqueueResponse(HTTP_OK, blobData);
-
-        try {
-            Uri uri = getServerUri(DEFAULT_FILENAME);
-            Request request = new Request(uri);
-
-            Uri localUri = Uri.fromFile(existentFile);
-            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
-            request.setDestinationUri(localUri);
-
-            long dlRequest = mDownloadManager.enqueue(request);
-
-            // wait for the download to complete
-            waitForDownloadOrTimeout(dlRequest);
-            Cursor cursor = getCursor(dlRequest);
-
-            try {
-                verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
-                verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
-                        DownloadManager.ERROR_FILE_ERROR);
-            } finally {
-                cursor.close();
-            }
-        } finally {
-            existentFile.delete();
-        }
-    }
-
-    /**
-     * Tests trying to download a file to SD card.
-     */
-    @LargeTest
-    public void testDownloadToExternal() throws Exception {
-        String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
-        File downloadedFile = new File(localDownloadDirectory, DEFAULT_FILENAME);
-        // make sure the file doesn't already exist in the directory
-        downloadedFile.delete();
-
-        try {
-            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
-
-            // Prepare the mock server with a standard response
-            enqueueResponse(HTTP_OK, blobData);
-
-            Uri uri = getServerUri(DEFAULT_FILENAME);
-            Request request = new Request(uri);
-
-            Uri localUri = Uri.fromFile(downloadedFile);
-            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
-            request.setDestinationUri(localUri);
-
-            long dlRequest = mDownloadManager.enqueue(request);
-
-            // wait for the download to complete
-            waitForDownloadOrTimeout(dlRequest);
-
-            verifyAndCleanupSingleFileDownload(dlRequest, blobData);
-
-            assertEquals(1, mReceiver.numDownloadsCompleted());
-        } finally {
-            downloadedFile.delete();
-        }
-    }
-
-    /**
-     * Tests trying to download a file to the system partition.
-     */
-    @LargeTest
-    public void testDownloadToProhibitedDirectory() throws Exception {
-        File downloadedFile = new File(PROHIBITED_DIRECTORY, DEFAULT_FILENAME);
-        try {
-            byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
-
-            // Prepare the mock server with a standard response
-            enqueueResponse(HTTP_OK, blobData);
-
-            Uri uri = getServerUri(DEFAULT_FILENAME);
-            Request request = new Request(uri);
-
-            Uri localUri = Uri.fromFile(downloadedFile);
-            Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
-            request.setDestinationUri(localUri);
-
-            try {
-                mDownloadManager.enqueue(request);
-                fail("Failed to throw SecurityException when trying to write to /system.");
-            } catch (SecurityException s) {
-                assertFalse(downloadedFile.exists());
-            }
-        } finally {
-            // Just in case file somehow got created, make sure to delete it
-            downloadedFile.delete();
-        }
-    }
-
-    /**
-     * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
-     * once Wifi is re-enabled.
-     */
-    @LargeTest
-    public void testDownloadNoWifi() throws Exception {
-        long timeout = 60 * 1000; // wait only 60 seconds before giving up
-        int fileSize = 140 * 1024;  // 140k
-        byte[] blobData = generateData(fileSize, DataType.TEXT);
-
-        setWiFiStateOn(false);
-        enqueueResponse(HTTP_OK, blobData);
-
-        try {
-            Uri uri = getServerUri(DEFAULT_FILENAME);
-            Request request = new Request(uri);
-            request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
-
-            long dlRequest = mDownloadManager.enqueue(request);
-
-            // wait for the download to complete
-            boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
-                    WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
-            assertFalse("Download proceeded without Wifi connection!", success);
-
-            setWiFiStateOn(true);
-            waitForDownloadOrTimeout(dlRequest);
-
-            assertEquals(1, mReceiver.numDownloadsCompleted());
-        } finally {
-            setWiFiStateOn(true);
-        }
-    }
-
-    /**
-     * Tests trying to download two large files (50M bytes, followed by 60M bytes)
-     */
-    @LargeTest
-    public void testInsufficientSpaceSingleFiles() throws Exception {
-        long fileSize1 = 50000000L;
-        long fileSize2 = 60000000L;
-        File largeFile1 = createFileOnSD(null, fileSize1, DataType.TEXT, null);
-        File largeFile2 = createFileOnSD(null, fileSize2, DataType.TEXT, null);
-
-        try {
-            long dlRequest = doStandardEnqueue(largeFile1);
-            waitForDownloadOrTimeout(dlRequest);
-            ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
-            verifyFileContents(pfd, largeFile1);
-            verifyFileSize(pfd, largeFile1.length());
-
-            dlRequest = doStandardEnqueue(largeFile2);
-            waitForDownloadOrTimeout(dlRequest);
-            Cursor cursor = getCursor(dlRequest);
-            try {
-                verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
-                        DownloadManager.ERROR_INSUFFICIENT_SPACE);
-            } finally {
-                cursor.close();
-            }
-        } finally {
-            largeFile1.delete();
-            largeFile2.delete();
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java
index 64cd6c4..5d01ba0 100644
--- a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java
+++ b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java
@@ -46,7 +46,7 @@
         String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090822\n"
                 + "RRULE:FREQ=YEARLY;WKST=SU";
         verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null,
-                null, null, 1250812800000L, null, "P1D", 1);
+                null, null, 1250812800000L, "UTC", "P1D", 1);
     }
 
     // Test 2 day all-day event
@@ -55,7 +55,7 @@
         String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090823\n"
                 + "RRULE:FREQ=YEARLY;WKST=SU";
         verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null,
-                null, null, 1250812800000L, null,  "P2D", 1);
+                null, null, 1250812800000L, "UTC",  "P2D", 1);
     }
 
     // run populateContentValues and verify the results
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index 0f5b090..53368d4 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -16,6 +16,9 @@
 
 package android.util;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
 import junit.framework.TestCase;
 
 import java.io.ByteArrayInputStream;
@@ -404,6 +407,14 @@
         }
     }
 
+    /** http://b/3026478 */
+    public void testSingleByteReads() throws IOException {
+        InputStream in = new Base64InputStream(
+                new ByteArrayInputStream("/v8=".getBytes()), Base64.DEFAULT);
+        assertEquals(254, in.read());
+        assertEquals(255, in.read());
+    }
+
     /**
      * Tests that Base64OutputStream produces exactly the same results
      * as calling Base64.encode/.decode on an in-memory array.
diff --git a/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
index ed280c9..df781bf 100644
--- a/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
+++ b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
@@ -54,8 +54,10 @@
     private static final String EXTERNAL_DOWNLOAD_URI_KEY = "external_download_uri";
     // Note: External environment variable ANDROID_TEST_EXTERNAL_URI must be set to point to the
     // external URI under which the files downloaded by the tests can be found. Note that the Uri
-    // must be accessible by the device during a test run.
-    private static String EXTERNAL_DOWNLOAD_URI_VALUE = null;
+    // must be accessible by the device during a test run. Correspondingly,
+    // ANDROID_TEST_EXTERNAL_LARGE_URI should point to the external URI of the folder containing
+    // large files.
+    private static String externalDownloadUriValue = null;
 
     Hashtable<String, String> mExtraParams = null;
 
@@ -69,8 +71,8 @@
         // ensure apk path has been set before test is run
         assertNotNull(getTestAppPath());
         mPMUtils = new PackageManagerHostTestUtils(getDevice());
-        EXTERNAL_DOWNLOAD_URI_VALUE = System.getenv("ANDROID_TEST_EXTERNAL_URI");
-        assertNotNull(EXTERNAL_DOWNLOAD_URI_VALUE);
+        externalDownloadUriValue = System.getenv("ANDROID_TEST_EXTERNAL_URI");
+        assertNotNull(externalDownloadUriValue);
         mExtraParams = getExtraParams();
     }
 
@@ -79,7 +81,7 @@
      */
     protected Hashtable<String, String> getExtraParams() {
         Hashtable<String, String> extraParams = new Hashtable<String, String>();
-        extraParams.put(EXTERNAL_DOWNLOAD_URI_KEY, EXTERNAL_DOWNLOAD_URI_VALUE);
+        extraParams.put(EXTERNAL_DOWNLOAD_URI_KEY, externalDownloadUriValue);
         return extraParams;
     }
 
@@ -198,4 +200,19 @@
                 DOWNLOAD_TEST_RUNNER_NAME, mExtraParams);
         assertTrue(testPassed);
     }
+
+    /**
+     * Spawns a device-based function to test 15 concurrent downloads of 5,000,000-byte files
+     *
+     * @throws Exception if the test failed at any point
+     */
+    public void testDownloadMultipleSimultaneously() throws Exception {
+        mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
+                File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
+
+        boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
+                FILE_DOWNLOAD_CLASS, "runDownloadMultipleSimultaneously",
+                DOWNLOAD_TEST_RUNNER_NAME, mExtraParams);
+        assertTrue(testPassed);
+    }
 }
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 ef81353..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;
@@ -35,6 +35,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
+import java.util.HashSet;
 
 import coretestutils.http.MockResponse;
 import coretestutils.http.MockWebServer;
@@ -55,8 +56,13 @@
     protected static String DOWNLOAD_10MB_FILENAME = "External10mb.apk";
     protected static long DOWNLOAD_10MB_FILESIZE = 10258741;
 
+    private static final String FILE_CONCURRENT_DOWNLOAD_FILE_PREFIX = "file";
+    private static final String FILE_CONCURRENT_DOWNLOAD_FILE_EXTENSION = ".bin";
+    protected static long CONCURRENT_DOWNLOAD_FILESIZE = 1000000;
+
     // Values to be obtained from TestRunner
     private String externalDownloadUriValue = null;
+    private String externalLargeDownloadUriValue = null;
 
     /**
      * {@inheritDoc }
@@ -65,12 +71,24 @@
     public void setUp() throws Exception {
         super.setUp();
         DownloadManagerTestRunner mRunner = (DownloadManagerTestRunner)getInstrumentation();
-        externalDownloadUriValue = mRunner.externalDownloadUriValue;
+        externalDownloadUriValue = normalizeUri(mRunner.externalDownloadUriValue);
         assertNotNull(externalDownloadUriValue);
 
-        if (!externalDownloadUriValue.endsWith("/")) {
-            externalDownloadUriValue += "/";
+        externalLargeDownloadUriValue = normalizeUri(mRunner.externalDownloadUriValue);
+        assertNotNull(externalLargeDownloadUriValue);
+    }
+
+    /**
+     * Normalizes a uri to ensure it ends with a "/"
+     *
+     * @param uri The uri to normalize (or null)
+     * @return The normalized uri, or null if null was passed in
+     */
+    public String normalizeUri(String uri) {
+        if (uri != null && !uri.endsWith("/")) {
+            uri += "/";
         }
+        return uri;
     }
 
     /**
@@ -223,7 +241,7 @@
 
         Uri remoteUri = getExternalFileUri(filename);
         Request request = new Request(remoteUri);
-        request.setMediaType(getMimeMapping(DownloadFileType.APK));
+        request.setMimeType(getMimeMapping(DownloadFileType.APK));
 
         dlRequest = mDownloadManager.enqueue(request);
 
@@ -460,4 +478,37 @@
             downloadedFile.delete();
         }
     }
+
+    /**
+     * Tests 15 concurrent downloads of 1,000,000-byte files.
+     *
+     * @throws Exception if test failed
+     */
+    public void runDownloadMultipleSimultaneously() throws Exception {
+        final int TOTAL_DOWNLOADS = 15;
+        HashSet<Long> downloadIds = new HashSet<Long>(TOTAL_DOWNLOADS);
+        MultipleDownloadsCompletedReceiver receiver = registerNewMultipleDownloadsReceiver();
+
+        // Make sure there are no pending downloads currently going on
+        removeAllCurrentDownloads();
+
+        try {
+            for (int i = 0; i < TOTAL_DOWNLOADS; ++i) {
+                long dlRequest = -1;
+                String filename = FILE_CONCURRENT_DOWNLOAD_FILE_PREFIX + i
+                        + FILE_CONCURRENT_DOWNLOAD_FILE_EXTENSION;
+                Uri remoteUri = getExternalFileUri(filename);
+                Request request = new Request(remoteUri);
+                request.setTitle(filename);
+                dlRequest = mDownloadManager.enqueue(request);
+                assertTrue(dlRequest != -1);
+                downloadIds.add(dlRequest);
+            }
+
+            waitForDownloadsOrTimeout(DEFAULT_WAIT_POLL_TIME, 15 * 60 * 2000);  // wait 15 mins max
+            assertEquals(TOTAL_DOWNLOADS, receiver.numDownloadsCompleted());
+        } finally {
+            removeAllCurrentDownloads();
+        }
+    }
 }
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java
index 0f16619..27bf7e1 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java
@@ -30,7 +30,7 @@
  *
  * To run the download manager tests:
  *
- * adb shell am instrument -e external_download_1mb_uri <uri> external_download_500k_uri <uri> \
+ * adb shell am instrument -e external_download_uri <uri> external_large_download_uri <uri> \
  *     -w com.android.frameworks.downloadmanagertests/.DownloadManagerTestRunner
  */
 
diff --git a/data/etc/android.hardware.audio.low_latency.xml b/data/etc/android.hardware.audio.low_latency.xml
new file mode 100644
index 0000000..677ec1c
--- /dev/null
+++ b/data/etc/android.hardware.audio.low_latency.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the feature indicating low-latency audio, as specified by the
+     CDD. ONLY devices that meet the CDD's requirements may declare this
+     feature. -->
+<permissions>
+    <feature name="android.hardware.audio.low_latency" />
+</permissions>
diff --git a/data/etc/android.hardware.sensor.barometer.xml b/data/etc/android.hardware.sensor.barometer.xml
new file mode 100644
index 0000000..ebd392d
--- /dev/null
+++ b/data/etc/android.hardware.sensor.barometer.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Feature for devices with barometer. -->
+<permissions>
+    <feature name="android.hardware.sensor.barometer" />
+</permissions>
diff --git a/data/etc/android.hardware.sensor.gyroscope.xml b/data/etc/android.hardware.sensor.gyroscope.xml
new file mode 100644
index 0000000..fe79632
--- /dev/null
+++ b/data/etc/android.hardware.sensor.gyroscope.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Feature for devices with gyroscope. -->
+<permissions>
+    <feature name="android.hardware.sensor.gyroscope" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index a3c9f6d..7f87b79 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -57,4 +57,7 @@
          android.hardware.sensor.proximity.xml -->
     <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
     <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
+    <!-- Devices that have low-latency audio stacks suitable for apps like
+         VoIP may include android.hardware.audio.low_latency.xml. ONLY apps
+         that meet the requirements specified in the CDD may include this. -->
 </permissions>
diff --git a/data/fonts/MTLc3m.ttf b/data/fonts/MTLc3m.ttf
new file mode 100644
index 0000000..3cc5c96
--- /dev/null
+++ b/data/fonts/MTLc3m.ttf
Binary files differ
diff --git a/data/fonts/MTLmr3m.ttf b/data/fonts/MTLmr3m.ttf
new file mode 100644
index 0000000..05b9093
--- /dev/null
+++ b/data/fonts/MTLmr3m.ttf
Binary files differ
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 6bcaaaf..1fd7bba 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -44,5 +44,5 @@
         <name>monaco</name>
     </font>
     <fallback ttf="DroidSansFallback" />
-    <fallback ttf="DroidSansJapanese" />
-</fonts>
\ No newline at end of file
+    <fallback ttf="MTLmr3m" />
+</fonts>
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index 035ddb9..9ebed56 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -434,7 +434,7 @@
 <h2 id="LayerList">Layer List</h2>
 
 <p>A {@link android.graphics.drawable.LayerDrawable} is a drawable object
-that manages an array of other drawables. Each drawable in the list is drawn in the order of the 
+that manages an array of other drawables. Each drawable in the list is drawn in the order of the
 list&mdash;the last drawable in the list is drawn on top.</p>
 
 <p>Each drawable is represented by an {@code &lt;item&gt;} element inside a single {@code
@@ -641,7 +641,7 @@
         android:state_checkable=["true" | "false"]
         android:state_checked=["true" | "false"]
         android:state_enabled=["true" | "false"]
-        android:state_window_focused=["true" | "false"] /> 
+        android:state_window_focused=["true" | "false"] />
 &lt;/selector>
 </pre>
 </dd>
@@ -729,12 +729,12 @@
 &lt;/selector>
 </pre>
 
-<p>This layout XML applies the drawable to a View:</p>
+<p>This layout XML applies the state list drawable to a Button:</p>
 <pre>
-&lt;ImageView
+&lt;Button
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
-    android:src="@drawable/button" />
+    android:background="@drawable/button" />
 </pre>
 </dd> <!-- end example -->
 
@@ -1670,13 +1670,13 @@
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
-    &lt;gradient 
-        android:startColor="#FFFF0000" 
+    &lt;gradient
+        android:startColor="#FFFF0000"
         android:endColor="#80FF00FF"
         android:angle="45"/>
-    &lt;padding android:left="7dp" 
+    &lt;padding android:left="7dp"
         android:top="7dp"
-        android:right="7dp" 
+        android:right="7dp"
         android:bottom="7dp" />
     &lt;corners android:radius="8dp" />
 &lt;/shape>
diff --git a/docs/html/guide/topics/security/security.jd b/docs/html/guide/topics/security/security.jd
index dbc9866..de0c6e5 100644
--- a/docs/html/guide/topics/security/security.jd
+++ b/docs/html/guide/topics/security/security.jd
@@ -21,14 +21,15 @@
 </div>
 </div>
 
-<p>Android is a multi-process system, in which each application (and parts of the
-system) runs in its own process.  Most security between applications and
-the system is enforced at the process level through standard Linux facilities,
-such as user and group IDs that are assigned to applications.
-Additional finer-grained security features are provided
-through a "permission" mechanism that enforces restrictions on the specific
-operations that a particular process can perform, and per-URI permissions
-for granting ad-hoc access to specific pieces of data.</p>
+<p>Android is a privilege-separated operating system, in which each
+application runs with a distinct system identity (Linux user ID and group
+ID).  Parts of the system are also separated into distinct identities.
+Linux thereby isolates applications from each other and from the system.</p>
+
+<p>Additional finer-grained security features are provided through a
+"permission" mechanism that enforces restrictions on the specific operations
+that a particular process can perform, and per-URI permissions for granting
+ad-hoc access to specific pieces of data.</p>
 
 <a name="arch"></a>
 <h2>Security Architecture</h2>
@@ -38,39 +39,46 @@
 adversely impact other applications, the operating system, or the user.  This
 includes reading or writing the user's private data (such as contacts or
 e-mails), reading or writing another application's files, performing
-network access, keeping the device awake, etc.<p>
+network access, keeping the device awake, etc.</p>
 
-<p>An application's process runs in a security sandbox. The sandbox is designed
-to prevent applications from disrupting each other, except by explicitly
-declaring the <em>permissions</em> they need for additional capabilities not
-provided by the basic sandbox. The system handles requests for permissions
-in various ways, typically by automatically allowing or disallowing based on
-certificates or by prompting the user.  The permissions required by an
-application are declared statically in that application, so they can be known
-up-front at install time and will not change after that.</p>
+<p>Because the kernel sandboxes applications from each other, applications
+must explicitly share resources and data. They do this by declaring the
+<em>permissions</em> they need for additional capabilities not provided by
+the basic sandbox. Applications statically declare the permissions they
+require, and the Android system prompts the user for consent at the time the
+application is installed. Android has no mechanism for granting permissions
+dynamically (at run-time) because it complicates the user experience to the
+detriment of security.</p>
+
+<p>The kernel is solely responsible for sandboxing applications from each
+other. In particular the Dalvik VM is not a security boundary, and any app
+can run native code (see <a href="/sdk/ndk/index.html">the Android NDK</a>).
+All types of applications &mdash; Java, native, and hybrid &mdash; are
+sandboxed in the same way and have the same degree of security from each
+other.</p>
 
 <a name="signing"></a>
 <h2>Application Signing</h2>
 
-<p>All Android applications (.apk files) must be signed with a certificate whose
-private key is held by their developer.  This certificate identifies the author
-of the application.  The certificate does <em>not</em> need to be signed by
-a certificate authority: it is perfectly allowable, and typical, for Android
-applications to use self-signed certificates.  The certificate is used only
-to establish trust relationships between applications, not for wholesale
-control over whether an application can be installed.  The most significant
-ways that signatures impact security is by determining who can access
-signature-based permissions and who can share user IDs.</p>
-
+<p>All Android applications (.apk files) must be signed with a certificate
+whose private key is held by their developer.  This certificate identifies
+the author of the application.  The certificate does <em>not</em> need to be
+signed by a certificate authority: it is perfectly allowable, and typical,
+for Android applications to use self-signed certificates. The purpose of
+certificates in Android is to distinguish application authors. This allows
+the system to grant or deny applications access to <a
+href="/guide/topics/manifest/permission-element.html#plevel">signature-level
+permissions</a> and to grant or deny an application's <a
+href="/guide/topics/manifest/manifest-element.html#uid">request to be given
+the same Linux identity</a> as another application.</p>
 
 <a name="userid"></a>
 <h2>User IDs and File Access</h2>
 
-<p>Each Android package (.apk) file installed on the device is given its
-own unique Linux user ID, creating a sandbox for it and preventing it from touching
-other applications (or other applications from touching it).  This user ID is
-assigned to it when the application is installed on the device, and
-remains constant for the duration of its life on that device.</p>
+<p>At install time, Android gives each package a distinct Linux user ID. The
+identity remains constant for the duration of the package's life on that
+device. On a different device, the same package may have a different UID;
+what matters is that each package has a distinct UID on a given device.</p>
 
 <p>Because security enforcement happens at the
 process level, the code of any two packages can not normally
@@ -150,7 +158,7 @@
 <li>Both sending and receiving broadcasts, to control who can receive
 your broadcast or who can send a broadcast to you.</li>
 <li>When accessing and operating on a content provider.</li>
-<li>Binding or starting a service.</li>
+<li>Binding to or starting a service.</li>
 </ul>
 
 
diff --git a/docs/html/sdk/adt_download.jd b/docs/html/sdk/adt_download.jd
index 5e642d7..3da576a 100644
--- a/docs/html/sdk/adt_download.jd
+++ b/docs/html/sdk/adt_download.jd
@@ -22,10 +22,17 @@
     <th>Notes</th>
   </tr>
   <tr>
+     <td>0.9.9</td>
+     <td><a href="http://dl-ssl.google.com/android/ADT-0.9.9.zip">ADT-0.9.9.zip</a></td>
+     <td><nobr>8301681 bytes</nobr></td>
+     <td>7deff0c9b25940a74cea7a0815a3bc36</td>
+     <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td>
+  </tr>
+  <tr>
      <td>0.9.8</td>
      <td><a href="http://dl-ssl.google.com/android/ADT-0.9.8.zip">ADT-0.9.8.zip</a></td>
-     <td><nobr>8703591 bytes</nobr></td>
-     <td>22070f8e52924605a3b3abf87c1ba39f</td>
+     <td><nobr>8301417 bytes</nobr></td>
+     <td>27e0de800512f13feae46fb554e6ee2f</td>
      <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td>
   </tr>
   <tr>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index a984d56..9f3c8b0 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,9 +1,9 @@
 page.title=ADT Plugin for Eclipse
 sdk.preview=0
-adt.zip.version=0.9.8
-adt.zip.download=ADT-0.9.8.zip
-adt.zip.bytes=8703591
-adt.zip.checksum=22070f8e52924605a3b3abf87c1ba39f
+adt.zip.version=0.9.9
+adt.zip.download=ADT-0.9.9.zip
+adt.zip.bytes=8301681
+adt.zip.checksum=7deff0c9b25940a74cea7a0815a3bc36
 
 @jd:body
 
@@ -100,11 +100,40 @@
 </style>
 
 
-
-
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ADT 0.9.9</a> <em>(September 2010)</em>
+  <div class="toggleme">
+
+
+</ul>
+</dd>
+
+<dl>
+
+<dt>Dependencies:</dt>
+
+<dd><p>ADT 0.9.9 replaces ADT 0.9.8 and is designed for use with SDK Tools r7
+and later. ADT 0.9.9 includes the ADT 0.9.8 features as well as an important
+bugfix, so we recommend that you upgrade as soon as possible. If you haven't
+already installed SDK Tools r7 into your SDK, use the Android SDK Manager to do
+so.</p></dd>
+
+<dt>General notes:</dt>
+<dd>
+<ul>
+<li>Fixes a problem in project import, in which source files were deleted in some cases.</li>
+<li>Includes all other ADT 0.9.8 features (see below).</li>
+</ul>
+</dd>
+</dl>
+ </div>
+</div>
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
 ADT 0.9.8</a> <em>(September 2010)</em>
   <div class="toggleme">
 
@@ -116,9 +145,7 @@
 
 <dt>Dependencies:</dt>
 
-<dd><p>ADT 0.9.8 is designed for use with SDK Tools r7 and later. Before 
-updating to ADT 0.9.8, we highly recommend that you use the Android SDK and
-AVD Manager to install SDK Tools r7 into your SDK.</p></dd>
+<dd><p>ADT 0.9.8 is now deprecated. Please use ADT 0.9.9 instead.</p></dd>
 
 <dt>General notes:</dt>
 <dd>
diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd
index d710b8e..7b11654 100644
--- a/docs/html/sdk/requirements.jd
+++ b/docs/html/sdk/requirements.jd
@@ -6,7 +6,7 @@
 
 <h3>Supported Operating Systems</h3>
 <ul>
-  <li>Windows XP (32-bit) or Vista (32- or 64-bit)</li>
+  <li>Windows XP (32-bit), Vista (32- or 64-bit), or Windows 7 (32- or 64-bit)</li>
   <li>Mac OS X 10.5.8 or later (x86 only)</li>
   <li>Linux (tested on Linux Ubuntu Hardy Heron)
     <ul>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index a665e95..fdf4438 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -94,7 +94,7 @@
       <span style="display:none" class="zh-TW"></span>
       </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.8
+      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.9
       <span style="display:none" class="de"></span>
       <span style="display:none" class="es"></span>
       <span style="display:none" class="fr"></span>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 6234f2c..7b49bc5 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -581,132 +581,6 @@
         nativeSetDefaultConfig(config.nativeInt);
     }
 
-    /**
-     * Create a LargeBitmap from the specified byte array.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param data byte array of compressed image data.
-     * @param offset offset into data for where the decoder should begin
-     *               parsing.
-     * @param length the number of bytes, beginning at offset, to parse
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(byte[] data,
-            int offset, int length, boolean isShareable) throws IOException {
-        if ((offset | length) < 0 || data.length < offset + length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        return nativeCreateLargeBitmap(data, offset, length, isShareable);
-    }
-
-    /**
-     * Create a LargeBitmap from the file descriptor.
-     * The position within the descriptor will not be changed when
-     * this returns, so the descriptor can be used again as is.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param fd The file descriptor containing the data to decode
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(
-            FileDescriptor fd, boolean isShareable) throws IOException {
-        return nativeCreateLargeBitmap(fd, isShareable);
-    }
-
-    /**
-     * Create a LargeBitmap from an input stream.
-     * The stream's position will be where ever it was after the encoded data
-     * was read.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param is The input stream that holds the raw data to be decoded into a
-     *           LargeBitmap.
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(InputStream is,
-            boolean isShareable) throws IOException {
-        // we need mark/reset to work properly in JNI
-
-        if (!is.markSupported()) {
-            is = new BufferedInputStream(is, 16 * 1024);
-        }
-
-        if (is instanceof AssetManager.AssetInputStream) {
-            return nativeCreateLargeBitmap(
-                    ((AssetManager.AssetInputStream) is).getAssetInt(),
-                    isShareable);
-        } else {
-            // pass some temp storage down to the native code. 1024 is made up,
-            // but should be large enough to avoid too many small calls back
-            // into is.read(...).
-            byte [] tempStorage = new byte[16 * 1024];
-            return nativeCreateLargeBitmap(is, tempStorage, isShareable);
-        }
-    }
-
-    /**
-     * Create a LargeBitmap from a file path.
-     * Currently only the Jpeg format is supported.
-     *
-     * @param pathName complete path name for the file to be decoded.
-     * @param isShareable If this is true, then the LargeBitmap may keep a
-     *                    shallow reference to the input. If this is false,
-     *                    then the LargeBitmap will explicitly make a copy of the
-     *                    input data, and keep that. Even if sharing is allowed,
-     *                    the implementation may still decide to make a deep
-     *                    copy of the input data. If an image is progressively encoded,
-     *                    allowing sharing may degrade the decoding speed.
-     * @return LargeBitmap, or null if the image data could not be decoded.
-     * @throws IOException if the image format is not supported or can not be decoded.
-     * @hide
-     */
-    public static LargeBitmap createLargeBitmap(String pathName, boolean isShareable)
-            throws IOException {
-        LargeBitmap bm = null;
-        InputStream stream = null;
-
-        try {
-            stream = new FileInputStream(pathName);
-            bm = createLargeBitmap(stream, isShareable);
-        } finally {
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    // do nothing here
-                }
-            }
-        }
-        return bm;
-    }
-
     private static native void nativeSetDefaultConfig(int nativeConfig);
     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
             Rect padding, Options opts);
@@ -716,14 +590,4 @@
     private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts);
     private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
-
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            byte[] data, int offset, int length, boolean isShareable);
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            FileDescriptor fd, boolean isShareable);
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            InputStream is, byte[] storage, boolean isShareable);
-    private static native LargeBitmap nativeCreateLargeBitmap(
-            int asset, boolean isShareable);
 }
-
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
new file mode 100644
index 0000000..454eb4a
--- /dev/null
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -0,0 +1,263 @@
+/* Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.content.res.AssetManager;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * BitmapRegionDecoder can be used to decode a rectangle region from an image.
+ * BitmapRegionDecoder is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * <p>To create a BitmapRegionDecoder, call newInstance(...).
+ * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ *
+ */
+public final class BitmapRegionDecoder {
+    private int mNativeBitmapRegionDecoder;
+    private boolean mRecycled;
+
+    /**
+     * Create a BitmapRegionDecoder from the specified byte array.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param data byte array of compressed image data.
+     * @param offset offset into data for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(byte[] data,
+            int offset, int length, boolean isShareable) throws IOException {
+        if ((offset | length) < 0 || data.length < offset + length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        return nativeNewInstance(data, offset, length, isShareable);
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from the file descriptor.
+     * The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as is.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param fd The file descriptor containing the data to decode
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(
+            FileDescriptor fd, boolean isShareable) throws IOException {
+        return nativeNewInstance(fd, isShareable);
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from an input stream.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           BitmapRegionDecoder.
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(InputStream is,
+            boolean isShareable) throws IOException {
+        // we need mark/reset to work properly in JNI
+
+        if (!is.markSupported()) {
+            is = new BufferedInputStream(is, 16 * 1024);
+        }
+
+        if (is instanceof AssetManager.AssetInputStream) {
+            return nativeNewInstance(
+                    ((AssetManager.AssetInputStream) is).getAssetInt(),
+                    isShareable);
+        } else {
+            // pass some temp storage down to the native code. 1024 is made up,
+            // but should be large enough to avoid too many small calls back
+            // into is.read(...).
+            byte [] tempStorage = new byte[16 * 1024];
+            return nativeNewInstance(is, tempStorage, isShareable);
+        }
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from a file path.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(String pathName,
+            boolean isShareable) throws IOException {
+        BitmapRegionDecoder decoder = null;
+        InputStream stream = null;
+
+        try {
+            stream = new FileInputStream(pathName);
+            decoder = newInstance(stream, isShareable);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // do nothing here
+                }
+            }
+        }
+        return decoder;
+    }
+
+    /*  Private constructor that must receive an already allocated native
+        region decoder int (pointer).
+
+        This can be called from JNI code.
+    */
+    private BitmapRegionDecoder(int decoder) {
+        mNativeBitmapRegionDecoder = decoder;
+        mRecycled = false;
+    }
+
+    /**
+     * Decodes a rectangle region in the image specified by rect.
+     *
+     * @param rect The rectangle that specified the region to be decode.
+     * @param options null-ok; Options that control downsampling.
+     *             inPurgeable is not supported.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded.
+     */
+    public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+        checkRecycled("decodeRegion called on recycled region decoder");
+        if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
+                || rect.bottom > getHeight())
+            throw new IllegalArgumentException("rectangle is not inside the image");
+        return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
+                rect.right - rect.left, rect.bottom - rect.top, options);
+    }
+
+    /** Returns the original image's width */
+    public int getWidth() {
+        checkRecycled("getWidth called on recycled region decoder");
+        return nativeGetWidth(mNativeBitmapRegionDecoder);
+    }
+
+    /** Returns the original image's height */
+    public int getHeight() {
+        checkRecycled("getHeight called on recycled region decoder");
+        return nativeGetHeight(mNativeBitmapRegionDecoder);
+    }
+
+    /**
+     * Frees up the memory associated with this region decoder, and mark the
+     * region decoder as "dead", meaning it will throw an exception if decodeRegion(),
+     * getWidth() or getHeight() is called.
+     *
+     * <p>This operation cannot be reversed, so it should only be called if you are
+     * sure there are no further uses for the region decoder. This is an advanced call,
+     * and normally need not be called, since the normal GC process will free up this
+     * memory when there are no more references to this region decoder.
+     */
+    public void recycle() {
+        if (!mRecycled) {
+            nativeClean(mNativeBitmapRegionDecoder);
+            mRecycled = true;
+        }
+    }
+
+    /**
+     * Returns true if this region decoder has been recycled.
+     * If so, then it is an error to try use its method.
+     *
+     * @return true if the region decoder has been recycled
+     */
+    public final boolean isRecycled() {
+        return mRecycled;
+    }
+
+    /**
+     * Called by methods that want to throw an exception if the region decoder
+     * has already been recycled.
+     */
+    private void checkRecycled(String errorMessage) {
+        if (mRecycled) {
+            throw new IllegalStateException(errorMessage);
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            recycle();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static native Bitmap nativeDecodeRegion(int lbm,
+            int start_x, int start_y, int width, int height,
+            BitmapFactory.Options options);
+    private static native int nativeGetWidth(int lbm);
+    private static native int nativeGetHeight(int lbm);
+    private static native void nativeClean(int lbm);
+
+    private static native BitmapRegionDecoder nativeNewInstance(
+            byte[] data, int offset, int length, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            FileDescriptor fd, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            InputStream is, byte[] storage, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            int asset, boolean isShareable);
+}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index bad94fb..00416d8 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -164,15 +164,17 @@
                     sdensity, tdensity);
             mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(),
                     sdensity, tdensity);
-            Rect dest = mPadding;
-            Rect src = mNinePatchState.mPadding;
-            if (dest == src) {
-                mPadding = dest = new Rect(src);
+            if (mNinePatchState.mPadding != null && mPadding != null) {
+                Rect dest = mPadding;
+                Rect src = mNinePatchState.mPadding;
+                if (dest == src) {
+                    mPadding = dest = new Rect(src);
+                }
+                dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
+                dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
+                dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
+                dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
             }
-            dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
-            dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
-            dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
-            dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
         }
     }
     
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 7c5371a..53039a0 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -380,12 +380,14 @@
     // continuously. Applications should not call
     // CameraHardwareInterface.autoFocus in this mode.
     static const char FOCUS_MODE_EDOF[];
-    // Continuous auto focus mode. The camera continuously tries to focus. This
-    // is ideal for shooting video or shooting photo of moving object. Auto
-    // focus starts when the parameter is set. Applications should not call
-    // CameraHardwareInterface.autoFocus in this mode.  To stop continuous
-    // focus, applications should change the focus mode to other modes.
-    static const char FOCUS_MODE_CONTINUOUS[];
+    // Continuous auto focus mode intended for video recording. The camera
+    // continuously tries to focus. This is ideal for shooting video.
+    // Applications still can call CameraHardwareInterface.takePicture in this
+    // mode but the subject may not be in focus. Auto focus starts when the
+    // parameter is set. Applications should not call
+    // CameraHardwareInterface.autoFocus in this mode. To stop continuous focus,
+    // applications should change the focus mode to other modes.
+    static const char FOCUS_MODE_CONTINUOUS_VIDEO[];
 
 private:
     DefaultKeyedVector<String8,String8>    mMap;
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/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
index 0f986a0..69021d8 100644
--- a/include/media/stagefright/foundation/ADebug.h
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -22,45 +22,18 @@
 
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AString.h>
+#include <utils/Log.h>
 
 namespace android {
 
-enum LogType {
-    VERBOSE,
-    INFO,
-    WARNING,
-    ERROR,
-    FATAL,
-};
-
-struct Logger {
-    Logger(LogType type);
-    virtual ~Logger();
-
-    template<class T> Logger &operator<<(const T &x) {
-        mMessage.append(x);
-
-        return *this;
-    }
-
-private:
-    android::AString mMessage;
-    LogType mLogType;
-
-    DISALLOW_EVIL_CONSTRUCTORS(Logger);
-};
-
-const char *LeafName(const char *s);
-
-#undef LOG
-#define LOG(type)    Logger(type) << LeafName(__FILE__) << ":" << __LINE__ << " "
+#define LITERAL_TO_STRING_INTERNAL(x)    #x
+#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
 
 #define CHECK(condition)                                \
-    do {                                                \
-        if (!(condition)) {                             \
-            LOG(FATAL) << "CHECK(" #condition ") failed.";    \
-        }                                               \
-    } while (false)
+    LOG_ALWAYS_FATAL_IF(                                \
+            !(condition),                               \
+            __FILE__ ":" LITERAL_TO_STRING(__LINE__)    \
+            " CHECK(" #condition ") failed.")
 
 #define MAKE_COMPARATOR(suffix,op)                          \
     template<class A, class B>                              \
@@ -85,8 +58,10 @@
     do {                                                                \
         AString ___res = Compare_##suffix(x, y);                        \
         if (!___res.empty()) {                                          \
-            LOG(FATAL) << "CHECK_" #suffix "(" #x "," #y ") failed: "   \
-                       << ___res;                                       \
+            LOG_ALWAYS_FATAL(                                           \
+                    __FILE__ ":" LITERAL_TO_STRING(__LINE__)            \
+                    " CHECK_" #suffix "( " #x "," #y ") failed: %s",    \
+                    ___res.c_str());                                    \
         }                                                               \
     } while (false)
 
@@ -97,7 +72,7 @@
 #define CHECK_GE(x,y)   CHECK_OP(x,y,GE,>=)
 #define CHECK_GT(x,y)   CHECK_OP(x,y,GT,>)
 
-#define TRESPASS()      LOG(FATAL) << "Should not be here."
+#define TRESPASS()      LOG_ALWAYS_FATAL("Should not be here.")
 
 }  // namespace android
 
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index dd44aa5..76307b2 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -110,6 +110,13 @@
      */
     virtual void bootFinished() = 0;
 
+    /* Capture the specified screen. requires READ_FRAME_BUFFER permission
+     * This function will fail if there is a secure window on screen.
+     */
+    virtual status_t captureScreen(DisplayID dpy,
+            sp<IMemoryHeap>* heap,
+            uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+
     /* Signal surfaceflinger that there might be some work to do
      * This is an ASYNCHRONOUS call.
      */
@@ -133,7 +140,8 @@
         SET_ORIENTATION,
         FREEZE_DISPLAY,
         UNFREEZE_DISPLAY,
-        SIGNAL
+        SIGNAL,
+        CAPTURE_SCREEN
     };
 
     virtual status_t    onTransact( uint32_t code,
diff --git a/include/ui/Input.h b/include/ui/Input.h
index b587e94..21baf32 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -40,10 +40,18 @@
 
 /*
  * Maximum number of pointers supported per motion event.
+ * Smallest number of pointers is 1.
  */
 #define MAX_POINTERS 10
 
 /*
+ * Maximum pointer id value supported in a motion event.
+ * Smallest pointer id is 0.
+ * (This is limited by our use of BitSet32 to track pointer assignments.)
+ */
+#define MAX_POINTER_ID 31
+
+/*
  * Declare a concrete type for the NDK's input event forward declaration.
  */
 struct AInputEvent {
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 96b4fae..cc16012 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -27,6 +27,7 @@
 #include <utils/String8.h>
 #include <utils/Looper.h>
 #include <utils/Pool.h>
+#include <utils/BitSet.h>
 
 #include <stddef.h>
 #include <unistd.h>
@@ -89,17 +90,13 @@
          * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
         FLAG_OUTSIDE = 0x02,
 
-        /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
-         * In the case of a key event, it should be delivered with flag
-         * AKEY_EVENT_FLAG_CANCELED set.
-         * In the case of a motion event, it should be delivered with action
-         * AMOTION_EVENT_ACTION_CANCEL instead. */
-        FLAG_CANCEL = 0x04,
-
         /* This flag indicates that the target of a MotionEvent is partly or wholly
          * obscured by another visible window above it.  The motion event should be
          * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
-        FLAG_WINDOW_IS_OBSCURED = 0x08,
+        FLAG_WINDOW_IS_OBSCURED = 0x04,
+
+        /* This flag indicates that a motion event is being split across multiple windows. */
+        FLAG_SPLIT = 0x08,
     };
 
     // The input channel to be targeted.
@@ -111,6 +108,13 @@
     // The x and y offset to add to a MotionEvent as it is delivered.
     // (ignored for KeyEvents)
     float xOffset, yOffset;
+
+    // The window type of the input target.
+    int32_t windowType;
+
+    // The subset of pointer ids to include in motion events dispatched to this input target
+    // if FLAG_SPLIT is set.
+    BitSet32 pointerIds;
 };
 
 
@@ -143,7 +147,7 @@
         FLAG_SHOW_WALLPAPER = 0x00100000,
         FLAG_TURN_SCREEN_ON = 0x00200000,
         FLAG_DISMISS_KEYGUARD = 0x00400000,
-        FLAG_IMMERSIVE = 0x00800000,
+        FLAG_SPLIT_TOUCH = 0x00800000,
         FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
         FLAG_COMPATIBLE_WINDOW = 0x20000000,
         FLAG_SYSTEM_ERROR = 0x40000000,
@@ -276,7 +280,7 @@
             const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
 
     /* Poke user activity for an event dispatched to a window. */
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) = 0;
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
 
     /* Checks whether a given application pid/uid has permission to inject input events
      * into other applications.
@@ -415,6 +419,16 @@
         T* prev;
     };
 
+    struct InjectionState {
+        mutable int32_t refCount;
+
+        int32_t injectorPid;
+        int32_t injectorUid;
+        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
+        bool injectionIsAsync; // set to true if injection is not waiting for the result
+        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
+    };
+
     struct EventEntry : Link<EventEntry> {
         enum {
             TYPE_SENTINEL,
@@ -423,21 +437,14 @@
             TYPE_MOTION
         };
 
-        int32_t refCount;
+        mutable int32_t refCount;
         int32_t type;
         nsecs_t eventTime;
-
-        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
-        bool    injectionIsAsync; // set to true if injection is not waiting for the result
-        int32_t injectorPid;      // -1 if not injected
-        int32_t injectorUid;      // -1 if not injected
+        InjectionState* injectionState;
 
         bool dispatchInProgress; // initially false, set to true while dispatching
-        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
 
-        inline bool isInjected() { return injectorPid >= 0; }
-
-        void recycle();
+        inline bool isInjected() { return injectionState != NULL; }
     };
 
     struct ConfigurationChangedEntry : EventEntry {
@@ -463,8 +470,6 @@
             INTERCEPT_KEY_RESULT_CONTINUE,
         };
         InterceptKeyResult interceptKeyResult; // set based on the interception result
-
-        void recycle();
     };
 
     struct MotionSample {
@@ -521,6 +526,10 @@
         inline bool hasForegroundTarget() const {
             return targetFlags & InputTarget::FLAG_FOREGROUND;
         }
+
+        inline bool isSplit() const {
+            return targetFlags & InputTarget::FLAG_SPLIT;
+        }
     };
 
     // A command entry captures state and behavior for an action to be performed in the
@@ -555,7 +564,6 @@
         KeyEntry* keyEntry;
         sp<InputChannel> inputChannel;
         sp<InputApplicationHandle> inputApplicationHandle;
-        int32_t windowType;
         int32_t userActivityEventType;
     };
 
@@ -611,6 +619,7 @@
     public:
         Allocator();
 
+        InjectionState* obtainInjectionState(int32_t injectorPid, int32_t injectorUid);
         ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
         KeyEntry* obtainKeyEntry(nsecs_t eventTime,
                 int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
@@ -626,6 +635,7 @@
                 int32_t targetFlags, float xOffset, float yOffset);
         CommandEntry* obtainCommandEntry(Command command);
 
+        void releaseInjectionState(InjectionState* injectionState);
         void releaseEventEntry(EventEntry* entry);
         void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
         void releaseKeyEntry(KeyEntry* entry);
@@ -633,10 +643,13 @@
         void releaseDispatchEntry(DispatchEntry* entry);
         void releaseCommandEntry(CommandEntry* entry);
 
+        void recycleKeyEntry(KeyEntry* entry);
+
         void appendMotionSample(MotionEntry* motionEntry,
                 nsecs_t eventTime, const PointerCoords* pointerCoords);
 
     private:
+        Pool<InjectionState> mInjectionStatePool;
         Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
         Pool<KeyEntry> mKeyEntryPool;
         Pool<MotionEntry> mMotionEntryPool;
@@ -645,6 +658,7 @@
         Pool<CommandEntry> mCommandEntryPool;
 
         void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
+        void releaseEventEntryInjectionState(EventEntry* entry);
     };
 
     /* Tracks dispatched key and motion event state so that cancelation events can be
@@ -823,6 +837,7 @@
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     Condition mInjectionSyncFinishedCondition;
+    void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
     void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
 
     // Throttling state.
@@ -858,23 +873,37 @@
     // Dispatch state.
     bool mDispatchEnabled;
     bool mDispatchFrozen;
+
     Vector<InputWindow> mWindows;
-    Vector<InputWindow*> mWallpaperWindows;
+
+    const InputWindow* getWindowLocked(const sp<InputChannel>& inputChannel);
 
     // Focus tracking for keys, trackball, etc.
-    InputWindow* mFocusedWindow;
+    const InputWindow* mFocusedWindow;
 
     // Focus tracking for touch.
-    bool mTouchDown;
-    InputWindow* mTouchedWindow;                   // primary target for current down
-    bool mTouchedWindowIsObscured;                 // true if other windows may obscure the target
-    Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
-    struct OutsideTarget {
-        InputWindow* window;
-        bool obscured;
+    struct TouchedWindow {
+        const InputWindow* window;
+        int32_t targetFlags;
+        BitSet32 pointerIds;
+        sp<InputChannel> channel;
     };
-    Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
-    Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+    struct TouchState {
+        bool down;
+        bool split;
+        Vector<TouchedWindow> windows;
+
+        TouchState();
+        ~TouchState();
+        void reset();
+        void copyFrom(const TouchState& other);
+        void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds);
+        void removeOutsideTouchWindows();
+        const InputWindow* getFirstForegroundWindow();
+    };
+
+    TouchState mTouchState;
+    TouchState mTempTouchState;
 
     // Focused application.
     InputApplication* mFocusedApplication;
@@ -899,8 +928,6 @@
     // The input targets that were most recently identified for dispatch.
     bool mCurrentInputTargetsValid; // false while targets are being recomputed
     Vector<InputTarget> mCurrentInputTargets;
-    int32_t mCurrentInputWindowType;
-    sp<InputChannel> mCurrentInputChannel;
 
     enum InputTargetWaitCause {
         INPUT_TARGET_WAIT_CAUSE_NONE,
@@ -915,7 +942,7 @@
 
     // Finding targets for input events.
     void resetTargetsLocked();
-    void commitTargetsLocked(const InputWindow* window);
+    void commitTargetsLocked();
     int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
             const InputApplication* application, const InputWindow* window,
             nsecs_t* nextWakeupTime);
@@ -924,19 +951,19 @@
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
     void resetANRTimeoutsLocked();
 
-    int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
-            nsecs_t* nextWakeupTime, InputWindow** outWindow);
-    int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
-            nsecs_t* nextWakeupTime, InputWindow** outWindow);
+    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+            nsecs_t* nextWakeupTime);
+    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+            nsecs_t* nextWakeupTime);
 
-    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
+    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+            BitSet32 pointerIds);
     void addMonitoringTargetsLocked();
-    void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
-    bool checkInjectionPermission(const InputWindow* window,
-            int32_t injectorPid, int32_t injectorUid);
+    bool shouldPokeUserActivityForCurrentInputTargetsLocked();
+    void pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType);
+    bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState);
     bool isWindowObscuredLocked(const InputWindow* window);
     bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
-    void releaseTouchedWindowLocked();
     String8 getApplicationWindowLabelLocked(const InputApplication* application,
             const InputWindow* window);
 
@@ -955,6 +982,9 @@
     void drainOutboundQueueLocked(Connection* connection);
     static int handleReceiveCallback(int receiveFd, int events, void* data);
 
+    // Splitting motion events across windows.
+    MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+
     // Dump state.
     void dumpDispatchStateLocked(String8& dump);
     void logDispatchStateLocked();
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 903c3c4..e85735a 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -549,10 +549,6 @@
             const int32_t* keyCodes, uint8_t* outFlags);
 
 protected:
-    /* Maximum pointer id value supported.
-     * (This is limited by our use of BitSet32 to track pointer assignments.) */
-    static const uint32_t MAX_POINTER_ID = 31;
-
     Mutex mLock;
 
     struct VirtualKey {
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index 19c8bf0..f5dbcd94 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -38,6 +38,9 @@
     // Clears the bit set.
     inline void clear() { value = 0; }
 
+    // Returns the number of marked bits in the set.
+    inline uint32_t count() const { return __builtin_popcount(value); }
+
     // Returns true if the bit set does not contain any marked bits.
     inline bool isEmpty() const { return ! value; }
 
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index 7d90866..3f00b78 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -205,6 +205,7 @@
 
     int pollInner(int timeoutMillis);
 
+    static void initTLSKey();
     static void threadDestructor(void *st);
 };
 
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 97d31f4..9668bde 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -24,8 +24,9 @@
 #ifndef __LIBS_ZIPFILERO_H
 #define __LIBS_ZIPFILERO_H
 
-#include "Errors.h"
-#include "FileMap.h"
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -211,6 +212,9 @@
     /* open Zip archive */
     int         mFd;
 
+    /* Lock for handling the file descriptor (seeks, etc) */
+    mutable Mutex mFdLock;
+
     /* zip file name */
     char*       mFileName;
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index a3e117f..13c58f0 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -517,12 +517,26 @@
     }
     
     if ((flags & TF_ONE_WAY) == 0) {
+        #if 0
+        if (code == 4) { // relayout
+            LOGI(">>>>>> CALLING transaction 4");
+        } else {
+            LOGI(">>>>>> CALLING transaction %d", code);
+        }
+        #endif
         if (reply) {
             err = waitForResponse(reply);
         } else {
             Parcel fakeReply;
             err = waitForResponse(&fakeReply);
         }
+        #if 0
+        if (code == 4) { // relayout
+            LOGI("<<<<<< RETURNING transaction 4");
+        } else {
+            LOGI("<<<<<< RETURNING transaction %d", code);
+        }
+        #endif
         
         IF_LOG_TRANSACTIONS() {
             TextOutput::Bundle _b(alog);
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 362d9ee..83e5e57f 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -142,7 +142,7 @@
 const char CameraParameters::FOCUS_MODE_MACRO[] = "macro";
 const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed";
 const char CameraParameters::FOCUS_MODE_EDOF[] = "edof";
-const char CameraParameters::FOCUS_MODE_CONTINUOUS[] = "continuous";
+const char CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO[] = "continuous-video";
 
 CameraParameters::CameraParameters()
                 : mMap()
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 5c111f6..040060e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -124,6 +124,21 @@
         remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
     }
 
+    virtual status_t captureScreen(DisplayID dpy,
+            sp<IMemoryHeap>* heap,
+            uint32_t* width, uint32_t* height, PixelFormat* format)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeInt32(dpy);
+        remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+        *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
+        *width = reply.readInt32();
+        *height = reply.readInt32();
+        *format = reply.readInt32();
+        return reply.readInt32();
+    }
+
     virtual void signal() const
     {
         Parcel data, reply;
@@ -190,6 +205,19 @@
             sp<IBinder> b = getCblk()->asBinder();
             reply->writeStrongBinder(b);
         } break;
+        case CAPTURE_SCREEN: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            DisplayID dpy = data.readInt32();
+            sp<IMemoryHeap> heap;
+            uint32_t w, h;
+            PixelFormat f;
+            status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+            reply->writeStrongBinder(heap->asBinder());
+            reply->writeInt32(w);
+            reply->writeInt32(h);
+            reply->writeInt32(f);
+            reply->writeInt32(res);
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 1cf7592..b5744b3e 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -69,6 +69,65 @@
     return value ? "true" : "false";
 }
 
+static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+    return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+            >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static bool isValidKeyAction(int32_t action) {
+    switch (action) {
+    case AKEY_EVENT_ACTION_DOWN:
+    case AKEY_EVENT_ACTION_UP:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool validateKeyEvent(int32_t action) {
+    if (! isValidKeyAction(action)) {
+        LOGE("Key event has invalid action code 0x%x", action);
+        return false;
+    }
+    return true;
+}
+
+static bool isValidMotionAction(int32_t action) {
+    switch (action & AMOTION_EVENT_ACTION_MASK) {
+    case AMOTION_EVENT_ACTION_DOWN:
+    case AMOTION_EVENT_ACTION_UP:
+    case AMOTION_EVENT_ACTION_CANCEL:
+    case AMOTION_EVENT_ACTION_MOVE:
+    case AMOTION_EVENT_ACTION_POINTER_DOWN:
+    case AMOTION_EVENT_ACTION_POINTER_UP:
+    case AMOTION_EVENT_ACTION_OUTSIDE:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool validateMotionEvent(int32_t action, size_t pointerCount,
+        const int32_t* pointerIds) {
+    if (! isValidMotionAction(action)) {
+        LOGE("Motion event has invalid action code 0x%x", action);
+        return false;
+    }
+    if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
+        LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.",
+                pointerCount, MAX_POINTERS);
+        return false;
+    }
+    for (size_t i = 0; i < pointerCount; i++) {
+        if (pointerIds[i] < 0 || pointerIds[i] > MAX_POINTER_ID) {
+            LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
+                    pointerIds[i], MAX_POINTER_ID);
+            return false;
+        }
+    }
+    return true;
+}
+
 
 // --- InputWindow ---
 
@@ -91,7 +150,7 @@
     mPolicy(policy),
     mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
     mDispatchEnabled(true), mDispatchFrozen(false),
-    mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+    mFocusedWindow(NULL),
     mFocusedApplication(NULL),
     mCurrentInputTargetsValid(false),
     mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
@@ -414,9 +473,10 @@
 }
 
 void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
-    if (entry->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
 #if DEBUG_DISPATCH_CYCLE
-        LOGD("Inbound event was dropped.  Setting injection result to failed.");
+        LOGD("Injected inbound event was dropped.");
 #endif
         setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
     }
@@ -424,10 +484,11 @@
 }
 
 bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
-    return ! entry->isInjected()
-            || entry->injectorUid == 0
+    InjectionState* injectionState = entry->injectionState;
+    return ! injectionState
+            || injectionState->injectorUid == 0
             || mPolicy->checkInjectEventsPermissionNonReentrant(
-                    entry->injectorPid, entry->injectorUid);
+                    injectionState->injectorPid, injectionState->injectorUid);
 }
 
 void InputDispatcher::resetKeyRepeatLocked() {
@@ -444,7 +505,7 @@
     // Reuse the repeated key entry if it is otherwise unreferenced.
     uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
-        entry->recycle();
+        mAllocator.recycleKeyEntry(entry);
         entry->eventTime = currentTime;
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
@@ -496,8 +557,7 @@
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         bool trusted;
         if (! dropEvent && mFocusedWindow) {
-            trusted = checkInjectionPermission(mFocusedWindow,
-                    entry->injectorPid, entry->injectorUid);
+            trusted = checkInjectionPermission(mFocusedWindow, entry->injectionState);
         } else {
             trusted = isEventFromReliableSourceLocked(entry);
         }
@@ -559,9 +619,8 @@
 
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
-        InputWindow* window = NULL;
-        int32_t injectionResult = findFocusedWindowLocked(currentTime,
-                entry, nextWakeupTime, & window);
+        int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
+                entry, nextWakeupTime);
         if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
             return false;
         }
@@ -572,14 +631,16 @@
         }
 
         addMonitoringTargetsLocked();
-        commitTargetsLocked(window);
+        commitTargetsLocked();
     }
 
     // Dispatch the key.
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 
     // Poke user activity.
-    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+    if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+        pokeUserActivityLocked(entry->eventTime, POWER_MANAGER_BUTTON_EVENT);
+    }
     return true;
 }
 
@@ -616,16 +677,15 @@
 
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
-        InputWindow* window = NULL;
         int32_t injectionResult;
         if (isPointerEvent) {
             // Pointer event.  (eg. touchscreen)
-            injectionResult = findTouchedWindowLocked(currentTime,
-                    entry, nextWakeupTime, & window);
+            injectionResult = findTouchedWindowTargetsLocked(currentTime,
+                    entry, nextWakeupTime);
         } else {
             // Non touch event.  (eg. trackball)
-            injectionResult = findFocusedWindowLocked(currentTime,
-                    entry, nextWakeupTime, & window);
+            injectionResult = findFocusedWindowTargetsLocked(currentTime,
+                    entry, nextWakeupTime);
         }
         if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
             return false;
@@ -637,34 +697,36 @@
         }
 
         addMonitoringTargetsLocked();
-        commitTargetsLocked(window);
+        commitTargetsLocked();
     }
 
     // Dispatch the motion.
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 
     // Poke user activity.
-    int32_t eventType;
-    if (isPointerEvent) {
-        switch (entry->action) {
-        case AMOTION_EVENT_ACTION_DOWN:
-            eventType = POWER_MANAGER_TOUCH_EVENT;
-            break;
-        case AMOTION_EVENT_ACTION_UP:
-            eventType = POWER_MANAGER_TOUCH_UP_EVENT;
-            break;
-        default:
-            if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+    if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+        int32_t eventType;
+        if (isPointerEvent) {
+            switch (entry->action) {
+            case AMOTION_EVENT_ACTION_DOWN:
                 eventType = POWER_MANAGER_TOUCH_EVENT;
-            } else {
-                eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+                break;
+            case AMOTION_EVENT_ACTION_UP:
+                eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+                break;
+            default:
+                if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+                    eventType = POWER_MANAGER_TOUCH_EVENT;
+                } else {
+                    eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+                }
+                break;
             }
-            break;
+        } else {
+            eventType = POWER_MANAGER_BUTTON_EVENT;
         }
-    } else {
-        eventType = POWER_MANAGER_BUTTON_EVENT;
+        pokeUserActivityLocked(entry->eventTime, eventType);
     }
-    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
     return true;
 }
 
@@ -735,13 +797,10 @@
 void InputDispatcher::resetTargetsLocked() {
     mCurrentInputTargetsValid = false;
     mCurrentInputTargets.clear();
-    mCurrentInputChannel.clear();
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
 }
 
-void InputDispatcher::commitTargetsLocked(const InputWindow* window) {
-    mCurrentInputWindowType = window->layoutParamsType;
-    mCurrentInputChannel = window->inputChannel;
+void InputDispatcher::commitTargetsLocked() {
     mCurrentInputTargetsValid = true;
 }
 
@@ -803,8 +862,8 @@
         // Give up.
         mInputTargetWaitTimeoutExpired = true;
 
-        // Release the touch target.
-        releaseTouchedWindowLocked();
+        // Release the touch targets.
+        mTouchState.reset();
 
         // Input state will not be realistic.  Mark it out of sync.
         if (inputChannel.get()) {
@@ -834,9 +893,8 @@
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
 }
 
-int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
-        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
-    *outWindow = NULL;
+int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
+        const EventEntry* entry, nsecs_t* nextWakeupTime) {
     mCurrentInputTargets.clear();
 
     int32_t injectionResult;
@@ -861,7 +919,7 @@
     }
 
     // Check permissions.
-    if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+    if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         goto Failed;
     }
@@ -888,8 +946,7 @@
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    *outWindow = mFocusedWindow;
-    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
+    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
 
     // Done.
 Failed:
@@ -905,15 +962,14 @@
     return injectionResult;
 }
 
-int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
-        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
+        const MotionEntry* entry, nsecs_t* nextWakeupTime) {
     enum InjectionPermission {
         INJECTION_PERMISSION_UNKNOWN,
         INJECTION_PERMISSION_GRANTED,
         INJECTION_PERMISSION_DENIED
     };
 
-    *outWindow = NULL;
     mCurrentInputTargets.clear();
 
     nsecs_t startTime = now();
@@ -945,25 +1001,33 @@
     bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
 
     int32_t action = entry->action;
+    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
-    int32_t injectionResult;
-    InjectionPermission injectionPermission;
-    if (action == AMOTION_EVENT_ACTION_DOWN) {
-        /* Case 1: ACTION_DOWN */
+    int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+        mTempTouchState.reset();
+        mTempTouchState.down = true;
+    } else {
+        mTempTouchState.copyFrom(mTouchState);
+    }
 
-        InputWindow* newTouchedWindow = NULL;
-        mTempTouchedOutsideTargets.clear();
+    bool isSplit = mTempTouchState.split && mTempTouchState.down;
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+            || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
+        /* Case 1: New splittable pointer going down. */
 
-        int32_t x = int32_t(entry->firstSample.pointerCoords[0].x);
-        int32_t y = int32_t(entry->firstSample.pointerCoords[0].y);
-        InputWindow* topErrorWindow = NULL;
-        bool obscured = false;
+        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+        int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x);
+        int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y);
+        const InputWindow* newTouchedWindow = NULL;
+        const InputWindow* topErrorWindow = NULL;
 
         // Traverse windows from front to back to find touched window and outside targets.
         size_t numWindows = mWindows.size();
         for (size_t i = 0; i < numWindows; i++) {
-            InputWindow* window = & mWindows.editItemAt(i);
+            const InputWindow* window = & mWindows.editItemAt(i);
             int32_t flags = window->layoutParamsFlags;
 
             if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
@@ -979,17 +1043,15 @@
                     if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
                         if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
                             newTouchedWindow = window;
-                            obscured = isWindowObscuredLocked(window);
                         }
                         break; // found touched window, exit window loop
                     }
                 }
 
-                if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) {
-                    OutsideTarget outsideTarget;
-                    outsideTarget.window = window;
-                    outsideTarget.obscured = isWindowObscuredLocked(window);
-                    mTempTouchedOutsideTargets.push(outsideTarget);
+                if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+                        && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                    mTempTouchState.addOrUpdateWindow(window,
+                            InputTarget::FLAG_OUTSIDE, BitSet32(0));
                 }
             }
         }
@@ -1007,6 +1069,21 @@
             goto Unresponsive;
         }
 
+        // Figure out whether splitting will be allowed for this window.
+        if (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+            // New window supports splitting.
+            isSplit = true;
+        } else if (isSplit) {
+            // New window does not support splitting but we have already split events.
+            // Assign the pointer to the first foreground window we find.
+            // (May be NULL which is why we put this code block before the next check.)
+            newTouchedWindow = mTempTouchState.getFirstForegroundWindow();
+        }
+        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
+        if (isSplit) {
+            targetFlags |= InputTarget::FLAG_SPLIT;
+        }
+
         // If we did not find a touched window then fail.
         if (! newTouchedWindow) {
             if (mFocusedApplication) {
@@ -1017,140 +1094,127 @@
 #endif
                 injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                         mFocusedApplication, NULL, nextWakeupTime);
-                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
                 goto Unresponsive;
             }
 
             LOGI("Dropping event because there is no touched window or focused application.");
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
             goto Failed;
         }
 
-        // Check permissions.
-        if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) {
-            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-            goto Failed;
+        // Update the temporary touch state.
+        BitSet32 pointerIds;
+        if (isSplit) {
+            uint32_t pointerId = entry->pointerIds[pointerIndex];
+            pointerIds.markBit(pointerId);
         }
-
-        // If the touched window is paused then keep waiting.
-        if (newTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Waiting because touched window is paused.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, newTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
-
-        // If the touched window is still working on previous events then keep waiting.
-        if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because touched window still processing previous input.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, newTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
-
-        // Success!  Update the touch dispatch state for real.
-        releaseTouchedWindowLocked();
-
-        mTouchedWindow = newTouchedWindow;
-        mTouchedWindowIsObscured = obscured;
-
-        if (newTouchedWindow->hasWallpaper) {
-            mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
-        }
+        mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds);
     } else {
-        /* Case 2: Everything but ACTION_DOWN */
-
-        // Check permissions.
-        if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) {
-            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-            goto Failed;
-        }
+        /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
 
         // If the pointer is not currently down, then ignore the event.
-        if (! mTouchDown) {
+        if (! mTempTouchState.down) {
             LOGI("Dropping event because the pointer is not down.");
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
             goto Failed;
         }
+    }
 
-        // If there is no currently touched window then fail.
-        if (! mTouchedWindow) {
+    // Check permission to inject into all touched foreground windows and ensure there
+    // is at least one touched foreground window.
+    {
+        bool haveForegroundWindow = false;
+        for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+            const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+            if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+                haveForegroundWindow = true;
+                if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) {
+                    injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                    injectionPermission = INJECTION_PERMISSION_DENIED;
+                    goto Failed;
+                }
+            }
+        }
+        if (! haveForegroundWindow) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Dropping event because there is no touched window to receive it.");
+            LOGD("Dropping event because there is no touched foreground window to receive it.");
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
             goto Failed;
         }
 
-        // If the touched window is paused then keep waiting.
-        if (mTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Waiting because touched window is paused.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, mTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
+        // Permission granted to injection into all touched foreground windows.
+        injectionPermission = INJECTION_PERMISSION_GRANTED;
+    }
 
-        // If the touched window is still working on previous events then keep waiting.
-        if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because touched window still processing previous input.");
+    // Ensure all touched foreground windows are ready for new input.
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+            // If the touched window is paused then keep waiting.
+            if (touchedWindow.window->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Waiting because touched window is paused.");
 #endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, mTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                        NULL, touchedWindow.window, nextWakeupTime);
+                goto Unresponsive;
+            }
+
+            // If the touched window is still working on previous events then keep waiting.
+            if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) {
+#if DEBUG_FOCUS
+                LOGD("Waiting because touched window still processing previous input.");
+#endif
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                        NULL, touchedWindow.window, nextWakeupTime);
+                goto Unresponsive;
+            }
+        }
+    }
+
+    // If this is the first pointer going down and the touched window has a wallpaper
+    // then also add the touched wallpaper windows so they are locked in for the duration
+    // of the touch gesture.
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+        const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
+        if (foregroundWindow->hasWallpaper) {
+            for (size_t i = 0; i < mWindows.size(); i++) {
+                const InputWindow* window = & mWindows[i];
+                if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
+                    mTempTouchState.addOrUpdateWindow(window, 0, BitSet32(0));
+                }
+            }
+        }
+    }
+
+    // If a touched window has been obscured at any point during the touch gesture, set
+    // the appropriate flag so we remember it for the entire gesture.
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+        if ((touchedWindow.targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) == 0) {
+            if (isWindowObscuredLocked(touchedWindow.window)) {
+                touchedWindow.targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+            }
         }
     }
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    injectionPermission = INJECTION_PERMISSION_GRANTED;
 
-    {
-        size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
-        for (size_t i = 0; i < numWallpaperWindows; i++) {
-            addWindowTargetLocked(mTouchedWallpaperWindows[i],
-                    InputTarget::FLAG_WINDOW_IS_OBSCURED);
-        }
-
-        size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
-        for (size_t i = 0; i < numOutsideTargets; i++) {
-            const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
-            int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
-            if (outsideTarget.obscured) {
-                outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-            }
-            addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
-        }
-        mTempTouchedOutsideTargets.clear();
-
-        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
-        if (mTouchedWindowIsObscured) {
-            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-        }
-        addWindowTargetLocked(mTouchedWindow, targetFlags);
-        *outWindow = mTouchedWindow;
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
+        addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags,
+                touchedWindow.pointerIds);
     }
 
+    // Drop the outside touch window since we will not care about them in the next iteration.
+    mTempTouchState.removeOutsideTouchWindows();
+
 Failed:
     // Check injection permission once and for all.
     if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
-                entry->injectorPid, entry->injectorUid)) {
+        if (checkInjectionPermission(NULL, entry->injectionState)) {
             injectionPermission = INJECTION_PERMISSION_GRANTED;
         } else {
             injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1159,25 +1223,41 @@
 
     // Update final pieces of touch state if the injector had permission.
     if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
-        if (action == AMOTION_EVENT_ACTION_DOWN) {
-            if (mTouchDown) {
-                // This is weird.  We got a down but we thought it was already down!
+        if (maskedAction == AMOTION_EVENT_ACTION_UP
+                || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+            // All pointers up or canceled.
+            mTempTouchState.reset();
+        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+            // First pointer went down.
+            if (mTouchState.down) {
                 LOGW("Pointer down received while already down.");
-            } else {
-                mTouchDown = true;
             }
+        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+            // One pointer went up.
+            if (isSplit) {
+                int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+                uint32_t pointerId = entry->pointerIds[pointerIndex];
 
-            if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-                // Since we failed to identify a target for this touch down, we may still
-                // be holding on to an earlier target from a previous touch down.  Release it.
-                releaseTouchedWindowLocked();
+                for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
+                    TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+                        touchedWindow.pointerIds.clearBit(pointerId);
+                        if (touchedWindow.pointerIds.isEmpty()) {
+                            mTempTouchState.windows.removeAt(i);
+                            continue;
+                        }
+                    }
+                    i += 1;
+                }
             }
-        } else if (action == AMOTION_EVENT_ACTION_UP) {
-            mTouchDown = false;
-            releaseTouchedWindowLocked();
         }
+
+        // Save changes to touch state.
+        mTouchState.copyFrom(mTempTouchState);
     } else {
-        LOGW("Not updating touch focus because injection was denied.");
+#if DEBUG_FOCUS
+        LOGD("Not updating touch focus because injection was denied.");
+#endif
     }
 
 Unresponsive:
@@ -1185,20 +1265,15 @@
     updateDispatchStatisticsLocked(currentTime, entry,
             injectionResult, timeSpentWaitingForApplication);
 #if DEBUG_FOCUS
-    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
-            "timeSpendWaitingForApplication=%0.1fms",
+    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
+            "timeSpentWaitingForApplication=%0.1fms",
             injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
 #endif
     return injectionResult;
 }
 
-void InputDispatcher::releaseTouchedWindowLocked() {
-    mTouchedWindow = NULL;
-    mTouchedWindowIsObscured = false;
-    mTouchedWallpaperWindows.clear();
-}
-
-void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+        BitSet32 pointerIds) {
     mCurrentInputTargets.push();
 
     InputTarget& target = mCurrentInputTargets.editTop();
@@ -1206,6 +1281,8 @@
     target.flags = targetFlags;
     target.xOffset = - window->frameLeft;
     target.yOffset = - window->frameTop;
+    target.windowType = window->layoutParamsType;
+    target.pointerIds = pointerIds;
 }
 
 void InputDispatcher::addMonitoringTargetsLocked() {
@@ -1217,22 +1294,27 @@
         target.flags = 0;
         target.xOffset = 0;
         target.yOffset = 0;
+        target.windowType = InputWindow::TYPE_SYSTEM_OVERLAY;
     }
 }
 
 bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
-        int32_t injectorPid, int32_t injectorUid) {
-    if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
-        bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+        const InjectionState* injectionState) {
+    if (injectionState
+            && injectionState->injectorUid > 0
+            && (window == NULL || window->ownerUid != injectionState->injectorUid)) {
+        bool result = mPolicy->checkInjectEventsPermissionNonReentrant(
+                injectionState->injectorPid, injectionState->injectorUid);
         if (! result) {
             if (window) {
                 LOGW("Permission denied: injecting event from pid %d uid %d to window "
                         "with input channel %s owned by uid %d",
-                        injectorPid, injectorUid, window->inputChannel->getName().string(),
+                        injectionState->injectorPid, injectionState->injectorUid,
+                        window->inputChannel->getName().string(),
                         window->ownerUid);
             } else {
                 LOGW("Permission denied: injecting event from pid %d uid %d",
-                        injectorPid, injectorUid);
+                        injectionState->injectorPid, injectionState->injectorUid);
             }
             return false;
         }
@@ -1282,12 +1364,19 @@
     }
 }
 
-void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
-        int32_t windowType, int32_t eventType) {
+bool InputDispatcher::shouldPokeUserActivityForCurrentInputTargetsLocked() {
+    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+        if (mCurrentInputTargets[i].windowType == InputWindow::TYPE_KEYGUARD) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType) {
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doPokeUserActivityLockedInterruptible);
     commandEntry->eventTime = eventTime;
-    commandEntry->windowType = windowType;
     commandEntry->userActivityEventType = eventType;
 }
 
@@ -1296,12 +1385,19 @@
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
-            "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
+            "xOffset=%f, yOffset=%f, "
+            "windowType=%d, pointerIds=0x%x, "
+            "resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
+            inputTarget->windowType, inputTarget->pointerIds.value,
             toString(resumeWithAppendedMotionSample));
 #endif
 
+    // Make sure we are never called for streaming when splitting across multiple windows.
+    bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
+    assert(! (resumeWithAppendedMotionSample && isSplit));
+
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
     if (connection->status != Connection::STATUS_NORMAL) {
@@ -1310,6 +1406,23 @@
         return;
     }
 
+    // Split a motion event if needed.
+    if (isSplit) {
+        assert(eventEntry->type == EventEntry::TYPE_MOTION);
+
+        MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
+        if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+            MotionEntry* splitMotionEntry = splitMotionEvent(
+                    originalMotionEntry, inputTarget->pointerIds);
+#if DEBUG_FOCUS
+            LOGD("channel '%s' ~ Split motion event.",
+                    connection->getInputChannelName());
+            logOutboundMotionDetailsLocked("  ", splitMotionEntry);
+#endif
+            eventEntry = splitMotionEntry;
+        }
+    }
+
     // Resume the dispatch cycle with a freshly appended motion sample.
     // First we check that the last dispatch entry in the outbound queue is for the same
     // motion event to which we appended the motion sample.  If we find such a dispatch
@@ -1351,7 +1464,8 @@
             // The dispatch entry is in progress and is still potentially open for streaming.
             // Try to stream the new motion sample.  This might fail if the consumer has already
             // consumed the motion event (or if the channel is broken).
-            MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+            MotionSample* appendedMotionSample = motionEntry->lastSample;
             status_t status = connection->inputPublisher.appendMotionSample(
                     appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
             if (status == OK) {
@@ -1426,7 +1540,7 @@
     DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
             inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
     if (dispatchEntry->hasForegroundTarget()) {
-        eventEntry->pendingForegroundDispatches += 1;
+        incrementPendingForegroundDispatchesLocked(eventEntry);
     }
 
     // Handle the case where we could not stream a new motion sample because the consumer has
@@ -1470,8 +1584,8 @@
     dispatchEntry->inProgress = true;
 
     // Update the connection's input state.
-    InputState::Consistency consistency = connection->inputState.trackEvent(
-            dispatchEntry->eventEntry);
+    EventEntry* eventEntry = dispatchEntry->eventEntry;
+    InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry);
 
 #if FILTER_INPUT_EVENTS
     // Filter out inconsistent sequences of input events.
@@ -1497,16 +1611,13 @@
 
     // Publish the event.
     status_t status;
-    switch (dispatchEntry->eventEntry->type) {
+    switch (eventEntry->type) {
     case EventEntry::TYPE_KEY: {
-        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+        KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
 
         // Apply target flags.
         int32_t action = keyEntry->action;
         int32_t flags = keyEntry->flags;
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
-            flags |= AKEY_EVENT_FLAG_CANCELED;
-        }
 
         // Publish the key event.
         status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
@@ -1524,7 +1635,7 @@
     }
 
     case EventEntry::TYPE_MOTION: {
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
         // Apply target flags.
         int32_t action = motionEntry->action;
@@ -1532,9 +1643,6 @@
         if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
             action = AMOTION_EVENT_ACTION_OUTSIDE;
         }
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
-            action = AMOTION_EVENT_ACTION_CANCEL;
-        }
         if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
             flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
         }
@@ -1617,7 +1725,7 @@
     }
 
     // Record information about the newly started dispatch cycle.
-    connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
+    connection->lastEventTime = eventEntry->eventTime;
     connection->lastDispatchTime = currentTime;
 
     // Notify other system components.
@@ -1773,6 +1881,86 @@
     } // release lock
 }
 
+InputDispatcher::MotionEntry*
+InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
+    assert(pointerIds.value != 0);
+
+    uint32_t splitPointerIndexMap[MAX_POINTERS];
+    int32_t splitPointerIds[MAX_POINTERS];
+    PointerCoords splitPointerCoords[MAX_POINTERS];
+
+    uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+    uint32_t splitPointerCount = 0;
+
+    for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
+            originalPointerIndex++) {
+        int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]);
+        if (pointerIds.hasBit(pointerId)) {
+            splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
+            splitPointerIds[splitPointerCount] = pointerId;
+            splitPointerCoords[splitPointerCount] =
+                    originalMotionEntry->firstSample.pointerCoords[originalPointerIndex];
+            splitPointerCount += 1;
+        }
+    }
+    assert(splitPointerCount == pointerIds.count());
+
+    int32_t action = originalMotionEntry->action;
+    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+    if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+            || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+        int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
+        int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex];
+        if (pointerIds.hasBit(pointerId)) {
+            if (pointerIds.count() == 1) {
+                // The first/last pointer went down/up.
+                action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+                        ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+            } else {
+                // A secondary pointer went down/up.
+                uint32_t splitPointerIndex = 0;
+                while (pointerId != splitPointerIds[splitPointerIndex]) {
+                    splitPointerIndex += 1;
+                }
+                action = maskedAction | (splitPointerIndex
+                        << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+            }
+        } else {
+            // An unrelated pointer changed.
+            action = AMOTION_EVENT_ACTION_MOVE;
+        }
+    }
+
+    MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry(
+            originalMotionEntry->eventTime,
+            originalMotionEntry->deviceId,
+            originalMotionEntry->source,
+            originalMotionEntry->policyFlags,
+            action,
+            originalMotionEntry->flags,
+            originalMotionEntry->metaState,
+            originalMotionEntry->edgeFlags,
+            originalMotionEntry->xPrecision,
+            originalMotionEntry->yPrecision,
+            originalMotionEntry->downTime,
+            splitPointerCount, splitPointerIds, splitPointerCoords);
+
+    for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next;
+            originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) {
+        for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount;
+                splitPointerIndex++) {
+            uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex];
+            splitPointerCoords[splitPointerIndex] =
+                    originalMotionSample->pointerCoords[originalPointerIndex];
+        }
+
+        mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime,
+                splitPointerCoords);
+    }
+
+    return splitMotionEntry;
+}
+
 void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
@@ -1800,6 +1988,9 @@
             eventTime, deviceId, source, policyFlags, action, flags,
             keyCode, scanCode, metaState, downTime);
 #endif
+    if (! validateKeyEvent(action)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1839,6 +2030,9 @@
                 pointerCoords[i].orientation);
     }
 #endif
+    if (! validateMotionEvent(action, pointerCount, pointerIds)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1913,8 +2107,10 @@
 
                     DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
                     if (! dispatchEntry->inProgress
-                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
-                        // No motion event is being dispatched.
+                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION
+                            || dispatchEntry->isSplit()) {
+                        // No motion event is being dispatched, or it is being split across
+                        // windows in which case we cannot stream.
                         continue;
                     }
 
@@ -1972,24 +2168,24 @@
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
 
-    EventEntry* injectedEntry;
+    InjectionState* injectionState;
     bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        injectedEntry = createEntryFromInjectedInputEventLocked(event);
+        EventEntry* injectedEntry = createEntryFromInjectedInputEventLocked(event);
         if (! injectedEntry) {
             return INPUT_EVENT_INJECTION_FAILED;
         }
 
-        injectedEntry->refCount += 1;
-        injectedEntry->injectorPid = injectorPid;
-        injectedEntry->injectorUid = injectorUid;
-
+        injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid);
         if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
-            injectedEntry->injectionIsAsync = true;
+            injectionState->injectionIsAsync = true;
         }
 
+        injectionState->refCount += 1;
+        injectedEntry->injectionState = injectionState;
+
         needWake = enqueueInboundEventLocked(injectedEntry);
     } // release lock
 
@@ -2005,7 +2201,7 @@
             injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
         } else {
             for (;;) {
-                injectionResult = injectedEntry->injectionResult;
+                injectionResult = injectionState->injectionResult;
                 if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
                     break;
                 }
@@ -2025,10 +2221,10 @@
 
             if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
                     && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
-                while (injectedEntry->pendingForegroundDispatches != 0) {
+                while (injectionState->pendingForegroundDispatches != 0) {
 #if DEBUG_INJECTION
                     LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
-                            injectedEntry->pendingForegroundDispatches);
+                            injectionState->pendingForegroundDispatches);
 #endif
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
@@ -2045,7 +2241,7 @@
             }
         }
 
-        mAllocator.releaseEventEntry(injectedEntry);
+        mAllocator.releaseInjectionState(injectionState);
     } // release lock
 
 #if DEBUG_INJECTION
@@ -2058,14 +2254,15 @@
 }
 
 void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
-    if (entry->isInjected()) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
 #if DEBUG_INJECTION
         LOGD("Setting input event injection result to %d.  "
                 "injectorPid=%d, injectorUid=%d",
-                 injectionResult, entry->injectorPid, entry->injectorUid);
+                 injectionResult, injectionState->injectorPid, injectionState->injectorUid);
 #endif
 
-        if (entry->injectionIsAsync) {
+        if (injectionState->injectionIsAsync) {
             // Log the outcome since the injector did not wait for the injection result.
             switch (injectionResult) {
             case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2083,41 +2280,26 @@
             }
         }
 
-        entry->injectionResult = injectionResult;
+        injectionState->injectionResult = injectionResult;
         mInjectionResultAvailableCondition.broadcast();
     }
 }
 
+void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
+        injectionState->pendingForegroundDispatches += 1;
+    }
+}
+
 void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
-    entry->pendingForegroundDispatches -= 1;
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
+        injectionState->pendingForegroundDispatches -= 1;
 
-    if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
-        mInjectionSyncFinishedCondition.broadcast();
-    }
-}
-
-static bool isValidKeyAction(int32_t action) {
-    switch (action) {
-    case AKEY_EVENT_ACTION_DOWN:
-    case AKEY_EVENT_ACTION_UP:
-        return true;
-    default:
-        return false;
-    }
-}
-
-static bool isValidMotionAction(int32_t action) {
-    switch (action & AMOTION_EVENT_ACTION_MASK) {
-    case AMOTION_EVENT_ACTION_DOWN:
-    case AMOTION_EVENT_ACTION_UP:
-    case AMOTION_EVENT_ACTION_CANCEL:
-    case AMOTION_EVENT_ACTION_MOVE:
-    case AMOTION_EVENT_ACTION_POINTER_DOWN:
-    case AMOTION_EVENT_ACTION_POINTER_UP:
-    case AMOTION_EVENT_ACTION_OUTSIDE:
-        return true;
-    default:
-        return false;
+        if (injectionState->pendingForegroundDispatches == 0) {
+            mInjectionSyncFinishedCondition.broadcast();
+        }
     }
 }
 
@@ -2126,9 +2308,7 @@
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY: {
         const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
-        if (! isValidKeyAction(keyEvent->getAction())) {
-            LOGE("Dropping injected key event since it has invalid action code 0x%x",
-                    keyEvent->getAction());
+        if (! validateKeyEvent(keyEvent->getAction())) {
             return NULL;
         }
 
@@ -2144,16 +2324,10 @@
 
     case AINPUT_EVENT_TYPE_MOTION: {
         const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
-        if (! isValidMotionAction(motionEvent->getAction())) {
-            LOGE("Dropping injected motion event since it has invalid action code 0x%x.",
-                    motionEvent->getAction());
+        if (! validateMotionEvent(motionEvent->getAction(),
+                motionEvent->getPointerCount(), motionEvent->getPointerIds())) {
             return NULL;
         }
-        if (motionEvent->getPointerCount() == 0
-                || motionEvent->getPointerCount() > MAX_POINTERS) {
-            LOGE("Dropping injected motion event since it has an invalid pointer count %d.",
-                    motionEvent->getPointerCount());
-        }
 
         uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
@@ -2182,6 +2356,16 @@
     }
 }
 
+const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) {
+    for (size_t i = 0; i < mWindows.size(); i++) {
+        const InputWindow* window = & mWindows[i];
+        if (window->inputChannel == inputChannel) {
+            return window;
+        }
+    }
+    return NULL;
+}
+
 void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
 #if DEBUG_FOCUS
     LOGD("setInputWindows");
@@ -2189,22 +2373,8 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        // Clear old window pointers but remember their associated channels.
+        // Clear old window pointers.
         mFocusedWindow = NULL;
-
-        sp<InputChannel> touchedWindowChannel;
-        if (mTouchedWindow) {
-            touchedWindowChannel = mTouchedWindow->inputChannel;
-            mTouchedWindow = NULL;
-        }
-        size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
-        if (numTouchedWallpapers != 0) {
-            for (size_t i = 0; i < numTouchedWallpapers; i++) {
-                mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
-            }
-            mTouchedWallpaperWindows.clear();
-        }
-        mWallpaperWindows.clear();
         mWindows.clear();
 
         // Loop over new windows and rebuild the necessary window pointers for
@@ -2213,26 +2383,23 @@
 
         size_t numWindows = mWindows.size();
         for (size_t i = 0; i < numWindows; i++) {
-            InputWindow* window = & mWindows.editItemAt(i);
+            const InputWindow* window = & mWindows.itemAt(i);
             if (window->hasFocus) {
                 mFocusedWindow = window;
-            }
-
-            if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
-                mWallpaperWindows.push(window);
-
-                for (size_t j = 0; j < numTouchedWallpapers; j++) {
-                    if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
-                        mTouchedWallpaperWindows.push(window);
-                    }
-                }
-            }
-
-            if (window->inputChannel == touchedWindowChannel) {
-                mTouchedWindow = window;
+                break;
             }
         }
-        mTempTouchedWallpaperChannels.clear();
+
+        for (size_t i = 0; i < mTouchState.windows.size(); ) {
+            TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
+            const InputWindow* window = getWindowLocked(touchedWindow.channel);
+            if (window) {
+                touchedWindow.window = window;
+                i += 1;
+            } else {
+                mTouchState.windows.removeAt(i);
+            }
+        }
 
 #if DEBUG_FOCUS
         logDispatchStateLocked();
@@ -2334,12 +2501,13 @@
     }
     dump.appendFormat("  focusedWindow: name='%s'\n",
             mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
-    dump.appendFormat("  touchedWindow: name='%s', touchDown=%d\n",
-            mTouchedWindow != NULL ? mTouchedWindow->name.string() : "<null>",
-            mTouchDown);
-    for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
-        dump.appendFormat("  touchedWallpaperWindows[%d]: name='%s'\n",
-                i, mTouchedWallpaperWindows[i]->name.string());
+    dump.appendFormat("  touchState: down=%s, split=%s\n", toString(mTouchState.down),
+            toString(mTouchState.split));
+    for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTouchState.windows[i];
+        dump.appendFormat("  touchedWindow[%d]: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+                i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
+                touchedWindow.targetFlags);
     }
     for (size_t i = 0; i < mWindows.size(); i++) {
         dump.appendFormat("  windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
@@ -2594,8 +2762,7 @@
 void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
-            commandEntry->userActivityEventType);
+    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
 
     mLock.lock();
 }
@@ -2627,17 +2794,32 @@
 InputDispatcher::Allocator::Allocator() {
 }
 
+InputDispatcher::InjectionState*
+InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) {
+    InjectionState* injectionState = mInjectionStatePool.alloc();
+    injectionState->refCount = 1;
+    injectionState->injectorPid = injectorPid;
+    injectionState->injectorUid = injectorUid;
+    injectionState->injectionIsAsync = false;
+    injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    injectionState->pendingForegroundDispatches = 0;
+    return injectionState;
+}
+
 void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
         nsecs_t eventTime) {
     entry->type = type;
     entry->refCount = 1;
     entry->dispatchInProgress = false;
     entry->eventTime = eventTime;
-    entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
-    entry->injectionIsAsync = false;
-    entry->injectorPid = -1;
-    entry->injectorUid = -1;
-    entry->pendingForegroundDispatches = 0;
+    entry->injectionState = NULL;
+}
+
+void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) {
+    if (entry->injectionState) {
+        releaseInjectionState(entry->injectionState);
+        entry->injectionState = NULL;
+    }
 }
 
 InputDispatcher::ConfigurationChangedEntry*
@@ -2720,6 +2902,15 @@
     return entry;
 }
 
+void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injectionState) {
+    injectionState->refCount -= 1;
+    if (injectionState->refCount == 0) {
+        mInjectionStatePool.free(injectionState);
+    } else {
+        assert(injectionState->refCount > 0);
+    }
+}
+
 void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
     switch (entry->type) {
     case EventEntry::TYPE_CONFIGURATION_CHANGED:
@@ -2741,6 +2932,7 @@
         ConfigurationChangedEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         mConfigurationChangeEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -2750,6 +2942,7 @@
 void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         mKeyEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -2759,6 +2952,7 @@
 void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
             MotionSample* next = sample->next;
             mMotionSamplePool.free(sample);
@@ -2793,22 +2987,12 @@
     motionEntry->lastSample = sample;
 }
 
+void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) {
+    releaseEventEntryInjectionState(keyEntry);
 
-// --- InputDispatcher::EventEntry ---
-
-void InputDispatcher::EventEntry::recycle() {
-    injectionResult = INPUT_EVENT_INJECTION_PENDING;
-    dispatchInProgress = false;
-    pendingForegroundDispatches = 0;
-}
-
-
-// --- InputDispatcher::KeyEntry ---
-
-void InputDispatcher::KeyEntry::recycle() {
-    EventEntry::recycle();
-    syntheticRepeat = false;
-    interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+    keyEntry->dispatchInProgress = false;
+    keyEntry->syntheticRepeat = false;
+    keyEntry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
 }
 
 
@@ -3057,6 +3241,72 @@
 }
 
 
+// --- InputDispatcher::TouchState ---
+
+InputDispatcher::TouchState::TouchState() :
+    down(false), split(false) {
+}
+
+InputDispatcher::TouchState::~TouchState() {
+}
+
+void InputDispatcher::TouchState::reset() {
+    down = false;
+    split = false;
+    windows.clear();
+}
+
+void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
+    down = other.down;
+    split = other.split;
+    windows.clear();
+    windows.appendVector(other.windows);
+}
+
+void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window,
+        int32_t targetFlags, BitSet32 pointerIds) {
+    if (targetFlags & InputTarget::FLAG_SPLIT) {
+        split = true;
+    }
+
+    for (size_t i = 0; i < windows.size(); i++) {
+        TouchedWindow& touchedWindow = windows.editItemAt(i);
+        if (touchedWindow.window == window) {
+            touchedWindow.targetFlags |= targetFlags;
+            touchedWindow.pointerIds.value |= pointerIds.value;
+            return;
+        }
+    }
+
+    windows.push();
+
+    TouchedWindow& touchedWindow = windows.editTop();
+    touchedWindow.window = window;
+    touchedWindow.targetFlags = targetFlags;
+    touchedWindow.pointerIds = pointerIds;
+    touchedWindow.channel = window->inputChannel;
+}
+
+void InputDispatcher::TouchState::removeOutsideTouchWindows() {
+    for (size_t i = 0 ; i < windows.size(); ) {
+        if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) {
+            windows.removeAt(i);
+        } else {
+            i += 1;
+        }
+    }
+}
+
+const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() {
+    for (size_t i = 0; i < windows.size(); i++) {
+        if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) {
+            return windows[i].window;
+        }
+    }
+    return NULL;
+}
+
+
 // --- InputDispatcherThread ---
 
 InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 783cbc4..f2b029a 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -3366,7 +3366,7 @@
                 if (id > MAX_POINTER_ID) {
 #if DEBUG_POINTERS
                     LOGD("Pointers: Ignoring driver provided pointer id %d because "
-                            "it is larger than max supported id %d for optimizations",
+                            "it is larger than max supported id %d",
                             id, MAX_POINTER_ID);
 #endif
                     havePointerIds = false;
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index b46279e..d2dd6eb 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -24,16 +24,15 @@
 
 namespace android {
 
-static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
-static bool gHaveTLS = false;
-static pthread_key_t gTLS = 0;
-
 // Hint for number of file descriptors to be associated with the epoll instance.
 static const int EPOLL_SIZE_HINT = 8;
 
 // Maximum number of file descriptors for which to retrieve poll events each iteration.
 static const int EPOLL_MAX_EVENTS = 16;
 
+static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
+static pthread_key_t gTLSKey = 0;
+
 Looper::Looper(bool allowNonCallbacks) :
         mAllowNonCallbacks(allowNonCallbacks),
         mResponseIndex(0) {
@@ -56,6 +55,7 @@
             errno);
 
     struct epoll_event eventItem;
+    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
     eventItem.events = EPOLLIN;
     eventItem.data.fd = mWakeReadPipeFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
@@ -69,6 +69,11 @@
     close(mEpollFd);
 }
 
+void Looper::initTLSKey() {
+    int result = pthread_key_create(& gTLSKey, threadDestructor);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+}
+
 void Looper::threadDestructor(void *st) {
     Looper* const self = static_cast<Looper*>(st);
     if (self != NULL) {
@@ -83,7 +88,7 @@
         looper->incStrong((void*)threadDestructor);
     }
 
-    pthread_setspecific(gTLS, looper.get());
+    pthread_setspecific(gTLSKey, looper.get());
 
     if (old != NULL) {
         old->decStrong((void*)threadDestructor);
@@ -91,17 +96,10 @@
 }
 
 sp<Looper> Looper::getForThread() {
-    if (!gHaveTLS) {
-        pthread_mutex_lock(&gTLSMutex);
-        if (pthread_key_create(&gTLS, threadDestructor) != 0) {
-            pthread_mutex_unlock(&gTLSMutex);
-            return NULL;
-        }
-        gHaveTLS = true;
-        pthread_mutex_unlock(&gTLSMutex);
-    }
+    int result = pthread_once(& gTLSOnce, initTLSKey);
+    LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
 
-    return (Looper*)pthread_getspecific(gTLS);
+    return (Looper*)pthread_getspecific(gTLSKey);
 }
 
 sp<Looper> Looper::prepare(int opts) {
@@ -331,6 +329,7 @@
         request.data = data;
 
         struct epoll_event eventItem;
+        memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
         eventItem.events = epollEvents;
         eventItem.data.fd = fd;
 
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 2d53136..9fcae72 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -22,6 +22,7 @@
 #include <utils/ZipFileRO.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
+#include <utils/threads.h>
 
 #include <zlib.h>
 
@@ -195,7 +196,7 @@
             free(scanBuf);
             return false;
         } else if (header != kLFHSignature) {
-            LOGV("Not a Zip archive (found 0x%08x)\n", val);
+            LOGV("Not a Zip archive (found 0x%08x)\n", header);
             free(scanBuf);
             return false;
         }
@@ -496,15 +497,21 @@
         }
 
         unsigned char lfhBuf[kLFHLen];
-        if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
-            LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
-            return false;
-        }
-        ssize_t actual =
-            TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
-        if (actual != sizeof(lfhBuf)) {
-            LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
-            return false;
+
+        {
+            AutoMutex _l(mFdLock);
+
+            if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+                LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+                return false;
+            }
+
+            ssize_t actual =
+                TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+            if (actual != sizeof(lfhBuf)) {
+                LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+                return false;
+            }
         }
 
         if (get4LE(lfhBuf) != kLFHSignature) {
@@ -636,7 +643,7 @@
         memcpy(buffer, ptr, uncompLen);
     } else {
         if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     if (compLen > kSequentialMin)
@@ -644,6 +651,8 @@
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
@@ -667,7 +676,7 @@
 
     getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
 
-    const FileMap* file = createEntryFileMap(entry);
+    FileMap* file = createEntryFileMap(entry);
     if (file == NULL) {
         goto bail;
     }
@@ -678,21 +687,23 @@
         ssize_t actual = write(fd, ptr, uncompLen);
         if (actual < 0) {
             LOGE("Write failed: %s\n", strerror(errno));
-            goto bail;
+            goto unmap;
         } else if ((size_t) actual != uncompLen) {
             LOGE("Partial write during uncompress (%zd of %zd)\n",
                 (size_t)actual, (size_t)uncompLen);
-            goto bail;
+            goto unmap;
         } else {
             LOGI("+++ successful write\n");
         }
     } else {
         if (!inflateBuffer(fd, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index 546bb9d..ac7eb8b 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -41,7 +41,7 @@
  * The Geocoder class requires a backend service that is not included in
  * the core android framework.  The Geocoder query methods will return an
  * empty list if there no backend service in the platform.  Use the
- * isImplemented() method to determine whether a Geocoder implementation
+ * isPresent() method to determine whether a Geocoder implementation
  * exists.
  */
 public final class Geocoder {
@@ -56,13 +56,13 @@
      * connectivity may still cause these methods to return null or
      * empty lists.
      */
-    public static Boolean isImplemented() {
+    public static boolean isPresent() {
         IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
         ILocationManager lm = ILocationManager.Stub.asInterface(b);
         try {
-            return lm.geocoderIsImplemented();
+            return lm.geocoderIsPresent();
         } catch (RemoteException e) {
-            Log.e(TAG, "isImplemented: got RemoteException", e);
+            Log.e(TAG, "isPresent: got RemoteException", e);
             return false;
         }
     }
diff --git a/location/java/android/location/GeocoderParams.java b/location/java/android/location/GeocoderParams.java
index 8b8e63b..174fe3e 100644
--- a/location/java/android/location/GeocoderParams.java
+++ b/location/java/android/location/GeocoderParams.java
@@ -29,6 +29,8 @@
  * as well as the Geocoder client's package name for geocoder server
  * logging.  This information is kept in a separate class to allow for
  * future expansion of the IGeocodeProvider interface.
+ *
+ * @hide
  */
 public class GeocoderParams implements Parcelable {
     private Locale mLocale;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 32d4b27..2255bf2 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -67,7 +67,7 @@
     // it need not be shared with other providers.
     void reportLocation(in Location location, boolean passive);
 
-    boolean geocoderIsImplemented();
+    boolean geocoderIsPresent();
     String getFromLocation(double latitude, double longitude, int maxResults,
         in GeocoderParams params, out List<Address> addrs);
     String getFromLocationName(String locationName,
diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/java/android/location/provider/GeocodeProvider.java
index 9a58763..493c631 100644
--- a/location/java/android/location/provider/GeocodeProvider.java
+++ b/location/java/android/location/provider/GeocodeProvider.java
@@ -29,6 +29,8 @@
  * outside of the core android platform.
  * Geocode providers can be implemented as services and return the result of
  * {@link GeocodeProvider#getBinder()} in its getBinder() method.
+ *
+ * @hide
  */
 public abstract class GeocodeProvider {
 
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java
index 95b4425..14dea14 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/java/android/location/provider/LocationProvider.java
@@ -34,6 +34,8 @@
  * outside of the core android platform.
  * Location providers can be implemented as services and return the result of
  * {@link LocationProvider#getBinder()} in its getBinder() method.
+ *
+ * @hide
  */
 public abstract class LocationProvider {
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 7469133..b97c3c4 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -963,14 +963,17 @@
     //--------------------
 
     /**
-     * Attaches an auxiliary effect to the audio track. A typical auxiliary effect is a
-     * reverberation effect which can be applied on any sound source that directs a certain
-     * amount of its energy to this effect. This amount is defined by setAuxEffectSendLevel().
+     * Attaches an auxiliary effect to the audio track. A typical auxiliary
+     * effect is a reverberation effect which can be applied on any sound source
+     * that directs a certain amount of its energy to this effect. This amount
+     * is defined by setAuxEffectSendLevel().
      * {@see #setAuxEffectSendLevel(float)}.
-     * <p>After creating an auxiliary effect (e.g. {@link android.media.EnvironmentalReverb}),
-     * retrieve its ID with {@link android.media.AudioEffect#getId()} and use it when calling
+     * <p>After creating an auxiliary effect (e.g.
+     * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
+     * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling
      * this method to attach the audio track to the effect.
-     * <p>To detach the effect from the audio track, call this method with a null effect id.
+     * <p>To detach the effect from the audio track, call this method with a
+     * null effect id.
      *
      * @param effectId system wide unique id of the effect to attach
      * @return error code or success, see {@link #SUCCESS},
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 280def9..cb46a29 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1210,9 +1210,10 @@
      * effect which can be applied on any sound source that directs a certain amount of its
      * energy to this effect. This amount is defined by setAuxEffectSendLevel().
      * {@see #setAuxEffectSendLevel(float)}.
-     * <p>After creating an auxiliary effect (e.g. {@link android.media.EnvironmentalReverb}),
-     * retrieve its ID with {@link android.media.AudioEffect#getId()} and use it when calling
-     * this method to attach the player to the effect.
+     * <p>After creating an auxiliary effect (e.g.
+     * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
+     * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
+     * to attach the player to the effect.
      * <p>To detach the effect from the player, call this method with a null effect id.
      * <p>This method must be called after one of the overloaded <code> setDataSource </code>
      * methods.
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
similarity index 84%
rename from media/java/android/media/AudioEffect.java
rename to media/java/android/media/audiofx/AudioEffect.java
index ae67114..3e54627 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -29,22 +29,35 @@
 import java.util.UUID;
 
 /**
- * AudioEffect is the base class for implementing audio effect control in Java
- * applications.
- * <p>Creating an AudioEffect object will create the effect engine in
- * audio framework if no instance of the same effect type exists in the
- * specified audio session. If one exists, this instance will be used.
- * <p>The application creating the AudioEffect object (or a derived class) will either
- * receive control of the effect engine or not depending on the priority
- * parameter. If priority is higher than the priority used by the current effect
- * engine owner, the control will be transfered to the new object. Otherwise
- * control will remain with the previous object. In this case, the new
- * application will be notified of changes in effect engine state or control
- * ownership by the appropiate listener.
- * <p>If the effect is to be applied to a specific AudioTrack or MediaPlayer instance,
- * the application must specify the audio session ID of that instance when calling the AudioEffect
- * constructor.
+ * AudioEffect is the base class for controlling audio effects provided by the android audio
+ * framework.
+ * <p>Applications should not use the AudioEffect class directly but one of its derived classes to
+ * control specific effects:
+ * <ul>
+ *   <li> {@link android.media.audiofx.Equalizer}</li>
+ *   <li> {@link android.media.audiofx.Virtualizer}</li>
+ *   <li> {@link android.media.audiofx.BassBoost}</li>
+ *   <li> {@link android.media.audiofx.PresetReverb}</li>
+ *   <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
+ * </ul>
+ * <p>If the audio effect is to be applied to a specific AudioTrack or MediaPlayer instance,
+ * the application must specify the audio session ID of that instance when creating the AudioEffect.
+ * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions).
+ * To apply an effect to the global audio output mix, session 0 must be specified when creating the
+ * AudioEffect.
+ * <p>Creating an effect on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio
+ * framework if no instance of the same effect type exists in the specified audio session.
+ * If one exists, this instance will be used.
+ * <p>The application creating the AudioEffect object (or a derived class) will either receive
+ * control of the effect engine or not depending on the priority parameter. If priority is higher
+ * than the priority used by the current effect engine owner, the control will be transfered to the
+ * new object. Otherwise control will remain with the previous object. In this case, the new
+ * application will be notified of changes in effect engine state or control ownership by the
+ * appropiate listener.
  */
+
 public class AudioEffect {
     static {
         System.loadLibrary("audioeffect_jni");
@@ -62,32 +75,38 @@
 
     /**
      * UUID for environmental reverb effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
             .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
     /**
      * UUID for preset reverb effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID
             .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
     /**
      * UUID for equalizer effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_EQUALIZER = UUID
             .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
     /**
      * UUID for bass boost effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_BASS_BOOST = UUID
             .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
     /**
      * UUID for virtualizer effect
+     * @hide
      */
     public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID
             .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
 
     /**
      * Null effect UUID. Used when the UUID for effect type of
+     * @hide
      */
     public static final UUID EFFECT_TYPE_NULL = UUID
             .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
@@ -95,10 +114,12 @@
     /**
      * State of an AudioEffect object that was not successfully initialized upon
      * creation
+     * @hide
      */
     public static final int STATE_UNINITIALIZED = 0;
     /**
      * State of an AudioEffect object that is ready to be used.
+     * @hide
      */
     public static final int STATE_INITIALIZED = 1;
 
@@ -106,14 +127,17 @@
     // frameworks/base/include/media/AudioEffect.h
     /**
      * Event id for engine control ownership change notification.
+     * @hide
      */
     public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
     /**
      * Event id for engine state change notification.
+     * @hide
      */
     public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
     /**
      * Event id for engine parameter change notification.
+     * @hide
      */
     public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
 
@@ -151,15 +175,17 @@
     public static final int ERROR_DEAD_OBJECT = -7;
 
     /**
-     * The effect descriptor contains necessary information to facilitate
-     * effects enumeration:<br>
+     * The effect descriptor contains information on a particular effect implemented in the
+     * audio framework:<br>
      * <ul>
-     *  <li>mType: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
-     *  <li>mUuid: UUID for this particular implementation</li>
-     *  <li>mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
-     *  <li>mName: human readable effect name</li>
-     *  <li>mImplementor: human readable effect implementor name</li>
+     *  <li>type: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
+     *  <li>uuid: UUID for this particular implementation</li>
+     *  <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
+     *  <li>name: human readable effect name</li>
+     *  <li>implementor: human readable effect implementor name</li>
      * </ul>
+     * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
+     * enumeration.
      */
     public static class Descriptor {
 
@@ -168,18 +194,39 @@
 
         public Descriptor(String type, String uuid, String connectMode,
                 String name, String implementor) {
-            mType = UUID.fromString(type);
-            mUuid = UUID.fromString(uuid);
-            mConnectMode = connectMode;
-            mName = name;
-            mImplementor = implementor;
+            this.type = UUID.fromString(type);
+            this.uuid = UUID.fromString(uuid);
+            this.connectMode = connectMode;
+            this.name = name;
+            this.implementor = implementor;
         }
 
-        public UUID mType;
-        public UUID mUuid;
-        public String mConnectMode;
-        public String mName;
-        public String mImplementor;
+        /**
+         *  Indicates the generic type of the effect (Equalizer, Bass boost ...). The UUID
+         *  corresponds to the OpenSL ES Interface ID for this type of effect.
+         */
+        public UUID type;
+        /**
+         *  Indicates the particular implementation of the effect in that type. Several effects
+         *  can have the same type but this uuid is unique to a given implementation.
+         */
+        public UUID uuid;
+        /**
+         *  Indicates if the effect is of insert category {@link #EFFECT_INSERT} or auxiliary
+         *  category {@link #EFFECT_AUXILIARY}. Insert effects (Typically an Equalizer) are applied
+         *  to the entire audio source and usually not shared by several sources. Auxiliary effects
+         *  (typically a reverberator) are applied to part of the signal (wet) and the effect output
+         *  is added to the original signal (dry).
+         */
+        public String connectMode;
+        /**
+         * Human readable effect name
+         */
+        public String name;
+        /**
+         * Human readable effect implementor name
+         */
+        public String implementor;
     };
 
     /**
@@ -242,10 +289,12 @@
     private OnParameterChangeListener mParameterChangeListener = null;
     /**
      * Lock to protect listeners updates against event notifications
+     * @hide
      */
     public final Object mListenerLock = new Object();
     /**
      * Handler for events coming from the native code
+     * @hide
      */
     public NativeEventHandler mNativeEventHandler = null;
 
@@ -283,6 +332,7 @@
      * @throws java.lang.IllegalArgumentException
      * @throws java.lang.UnsupportedOperationException
      * @throws java.lang.RuntimeException
+     * @hide
      */
 
     public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
@@ -337,7 +387,7 @@
     /**
      * Get the effect descriptor.
      *
-     * @see android.media.AudioEffect.Descriptor
+     * @see android.media.audiofx.AudioEffect.Descriptor
      * @throws IllegalStateException
      */
     public Descriptor getDescriptor() throws IllegalStateException {
@@ -351,7 +401,7 @@
 
     /**
      * Query all effects available on the platform. Returns an array of
-     * {@link android.media.AudioEffect.Descriptor} objects
+     * {@link android.media.audiofx.AudioEffect.Descriptor} objects
      *
      * @throws IllegalStateException
      */
@@ -365,7 +415,11 @@
     // --------------------
 
     /**
-     * Enable or disable effect engine.
+     * Enable or disable the effect.
+     * Creating an audio effect does not automatically apply this effect on the audio source. It
+     * creates the resources necessary to process this effect but the audio signal is still bypassed
+     * through the effect engine. Calling this method will make that the effect is actually applied
+     * or not to the audio content being played in the corresponding audio session.
      *
      * @param enabled the requested enable state
      * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
@@ -392,6 +446,7 @@
      *         {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
      *         {@link #ERROR_DEAD_OBJECT} in case of failure
      * @throws IllegalStateException
+     * @hide
      */
     public int setParameter(byte[] param, byte[] value)
             throws IllegalStateException {
@@ -403,6 +458,7 @@
      * Set effect parameter. The parameter and its value are integers.
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int param, int value) throws IllegalStateException {
         byte[] p = intToByteArray(param);
@@ -415,6 +471,7 @@
      * short integer.
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int param, short value)
             throws IllegalStateException {
@@ -428,6 +485,7 @@
      * array of bytes.
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int param, byte[] value)
             throws IllegalStateException {
@@ -440,6 +498,7 @@
      * the value is also an array of 1 or 2 integers
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int[] param, int[] value)
             throws IllegalStateException {
@@ -464,6 +523,7 @@
      * the value is an array of 1 or 2 short integers
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int[] param, short[] value)
             throws IllegalStateException {
@@ -489,6 +549,7 @@
      * the value is an array of bytes
      *
      * @see #setParameter(byte[], byte[])
+     * @hide
      */
     public int setParameter(int[] param, byte[] value)
             throws IllegalStateException {
@@ -519,6 +580,7 @@
      *         returning, value.length is updated with the actual size of the
      *         returned value.
      * @throws IllegalStateException
+     * @hide
      */
     public int getParameter(byte[] param, byte[] value)
             throws IllegalStateException {
@@ -539,6 +601,7 @@
      * array of bytes.
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int param, byte[] value)
             throws IllegalStateException {
@@ -552,6 +615,7 @@
      * array of 1 or 2 integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int param, int[] value)
             throws IllegalStateException {
@@ -576,6 +640,7 @@
      * array of 1 or 2 short integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int param, short[] value)
             throws IllegalStateException {
@@ -600,6 +665,7 @@
      * the value is also an array of 1 or 2 integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int[] param, int[] value)
             throws IllegalStateException {
@@ -627,6 +693,7 @@
      * the value is an array of 1 or 2 short integers
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int[] param, short[] value)
             throws IllegalStateException {
@@ -654,6 +721,7 @@
      * the value is an array of bytes
      *
      * @see #getParameter(byte[], byte[])
+     * @hide
      */
     public int getParameter(int[] param, byte[] value)
             throws IllegalStateException {
@@ -673,6 +741,7 @@
      * Send a command to the effect engine. This method is intended to send
      * proprietary commands to a particular effect implementation.
      *
+     * @hide
      */
     public int command(int cmdCode, byte[] command, byte[] reply)
             throws IllegalStateException {
@@ -709,7 +778,7 @@
     }
 
     /**
-     * Returns effect engine enable state
+     * Returns effect enabled state
      *
      * @return true if the effect is enabled, false otherwise.
      * @throws IllegalStateException
@@ -768,6 +837,7 @@
      * Sets the listener AudioEffect notifies when a parameter is changed.
      *
      * @param listener
+     * @hide
      */
     public void setParameterListener(OnParameterChangeListener listener) {
         synchronized (mListenerLock) {
@@ -828,6 +898,7 @@
     /**
      * The OnParameterChangeListener interface defines a method called by the AudioEffect
      * when a parameter is changed in the effect engine by the controlling application.
+     * @hide
      */
     public interface OnParameterChangeListener {
         /**
@@ -847,39 +918,40 @@
     // -------------------------------------------------------------------------
 
     /**
-     *  This intent launches an audio effect control panel UI. The goal of this intent is to enable
-     *  separate implementations of music/media player applications and audio effect control
-     *  application or services. This will allow platform vendors to offer more advanced control
-     *  options for standard effects or control for platform specific effects.
+     *  Intent to launch an audio effect control panel UI.
+     *  <p>The goal of this intent is to enable separate implementations of music/media player
+     *  applications and audio effect control application or services.
+     *  This will allow platform vendors to offer more advanced control options for standard effects
+     *  or control for platform specific effects.
      *  <p>The intent carries a number of extras used by the player application to communicate
      *  necessary pieces of information to the control panel application.
      *  <p>The calling application must use the
      *  {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the
      *  control panel so that its package name is indicated and used by the control panel
      *  application to keep track of changes for this particular application.
-     *  <p>The android.media.EXTRA_AUDIO_SESSION extra will indicate an audio session to which the
+     *  <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the
      *  audio effects should be applied. If no audio session is specified, either one of the
      *  follownig will happen:
-     *  - If an audio session was previously opened by the calling application with
+     *  <p>- If an audio session was previously opened by the calling application with
      *  {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will
      *  be applied to that session.
-     *  - If no audio session is opened, the changes will be stored in the package specific storage
-     *  area and applied whenever a new audio session is opened by this application.
-     *  <p>The android.media.EXTRA_CONTENT_TYPE extra will help the control panel application
+     *  <p>- If no audio session is opened, the changes will be stored in the package specific
+     *  storage area and applied whenever a new audio session is opened by this application.
+     *  <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application
      *  customize both the UI layout and the default audio effect settings if none are already
      *  stored for the calling application.
-     *  {@hide} pending API council approval
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL =
         "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL";
 
     /**
-     *  This intent indicates to the effect control application or service that a new audio session
-     *  is opened and requires audio effects to be applied. This is different from
-     *  {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no UI should be displayed in
-     *  this case. Music player applications can broadcast this intent before starting playback
-     *  to make sure that any audio effect settings previously selected by the user are applied.
+     *  Intent to signal to the effect control application or service that a new audio session
+     *  is opened and requires audio effects to be applied.
+     *  <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no
+     *  UI should be displayed in this case. Music player applications can broadcast this intent
+     *  before starting playback to make sure that any audio effect settings previously selected
+     *  by the user are applied.
      *  <p>The effect control application receiving this intent will look for previously stored
      *  settings for the calling application, create all required audio effects and apply the
      *  effect settings to the specified audio session.
@@ -888,48 +960,49 @@
      *  <p>If no stored settings are found for the calling application, default settings for the
      *  content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings
      *  for a given content type are platform specific.
-     *  {@hide} pending API council approval
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION =
         "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION";
 
     /**
-     *  This intent indicates to the effect control application or service that an audio session
+     *  Intent to signal to the effect control application or service that an audio session
      *  is closed and that effects should not be applied anymore.
-     *  <p>The effect control application receiving this intent will delete all effects on this
-     *  session and store current settings in package specific storage.
+     *  <p>The effect control application receiving this intent will delete all effects on
+     *  this session and store current settings in package specific storage.
      *  <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
      *  audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
      *  <p>It is good practice for applications to broadcast this intent when music playback stops
      *  and/or when exiting to free system resources consumed by audio effect engines.
-     *  {@hide} pending API council approval
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION =
         "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION";
 
     /**
-     * This extra indicates the ID of the audio session the effects should be applied to.
+     * Contains the ID of the audio session the effects should be applied to.
+     * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL},
+     * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+     * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
      * <p>The extra value is of type int and is the audio session ID.
-     *  @see android.media.MediaPlayer#setAudioSessionId(int) for details on audio sessions.
-     *  {@hide} pending API council approval
+     *  @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions.
      */
      public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION";
 
     /**
-     * This extra indicates the package name of the calling application for
-     * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+     * Contains the package name of the calling application.
+     * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
      * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
      * <p>The extra value is a string containing the full package name.
-     *  {@hide} pending API council approval
      */
     public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME";
 
     /**
-     * This extra indicates which type of content is played by the application. This information is
-     * used by the effect control application to customize UI and default effect settings.
-     * The content type is one of the following:
+     * Indicates which type of content is played by the application.
+     * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and
+     * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents.
+     * <p>This information is used by the effect control application to customize UI and select
+     * appropriate default effect settings. The content type is one of the following:
      * <ul>
      *   <li>{@link #CONTENT_TYPE_MUSIC}</li>
      *   <li>{@link #CONTENT_TYPE_MOVIE}</li>
@@ -937,28 +1010,23 @@
      *   <li>{@link #CONTENT_TYPE_VOICE}</li>
      * </ul>
      * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}.
-     *  {@hide} pending API council approval
      */
     public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE";
 
     /**
      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music
-     *  {@hide} pending API council approval
      */
     public static final int  CONTENT_TYPE_MUSIC = 0;
     /**
-     * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video of movie
-     *  {@hide} pending API council approval
+     * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie
      */
     public static final int  CONTENT_TYPE_MOVIE = 1;
     /**
      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio
-     *  {@hide} pending API council approval
      */
     public static final int  CONTENT_TYPE_GAME = 2;
     /**
      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio
-     *  {@hide} pending API council approval
      */
     public static final int  CONTENT_TYPE_VOICE = 3;
 
@@ -1088,6 +1156,9 @@
     // Utility methods
     // ------------------
 
+    /**
+    * @hide
+    */
     public void checkState(String methodName) throws IllegalStateException {
         synchronized (mStateLock) {
             if (mState != STATE_INITIALIZED) {
@@ -1097,6 +1168,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public void checkStatus(int status) {
         switch (status) {
         case AudioEffect.SUCCESS:
@@ -1112,11 +1186,17 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public int byteArrayToInt(byte[] valueBuf) {
         return byteArrayToInt(valueBuf, 0);
 
     }
 
+    /**
+     * @hide
+     */
     public int byteArrayToInt(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
@@ -1124,6 +1204,9 @@
 
     }
 
+    /**
+     * @hide
+     */
     public byte[] intToByteArray(int value) {
         ByteBuffer converter = ByteBuffer.allocate(4);
         converter.order(ByteOrder.nativeOrder());
@@ -1131,10 +1214,16 @@
         return converter.array();
     }
 
+    /**
+     * @hide
+     */
     public short byteArrayToShort(byte[] valueBuf) {
         return byteArrayToShort(valueBuf, 0);
     }
 
+    /**
+     * @hide
+     */
     public short byteArrayToShort(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
@@ -1142,6 +1231,9 @@
 
     }
 
+    /**
+     * @hide
+     */
     public byte[] shortToByteArray(short value) {
         ByteBuffer converter = ByteBuffer.allocate(2);
         converter.order(ByteOrder.nativeOrder());
@@ -1150,6 +1242,9 @@
         return converter.array();
     }
 
+    /**
+     * @hide
+     */
     public byte[] concatArrays(byte[]... arrays) {
         int len = 0;
         for (byte[] a : arrays) {
@@ -1164,5 +1259,4 @@
         }
         return b;
     }
-
 }
diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/audiofx/BassBoost.java
similarity index 94%
rename from media/java/android/media/BassBoost.java
rename to media/java/android/media/audiofx/BassBoost.java
index 476b056..ca55f0f 100644
--- a/media/java/android/media/BassBoost.java
+++ b/media/java/android/media/audiofx/BassBoost.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -32,15 +32,19 @@
 /**
  * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable
  * to a simple equalizer but limited to one band amplification in the low frequency range.
- * <p>An application creates a BassBoost object to instantiate and control a bass boost engine
- * in the audio framework.
+ * <p>An application creates a BassBoost object to instantiate and control a bass boost engine in
+ * the audio framework.
  * <p>The methods, parameter types and units exposed by the BassBoost implementation are directly
  * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
  * for the SLBassBoostItf interface. Please refer to this specification for more details.
  * <p>To attach the BassBoost to a particular AudioTrack or MediaPlayer, specify the audio session
- * ID of this AudioTrack or MediaPlayer when constructing the BassBoost. If the audio session ID 0
- * is specified, the BassBoost applies to the main audio output mix.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * ID of this AudioTrack or MediaPlayer when constructing the BassBoost.
+ * If the audio session ID 0 is specified, the BassBoost applies to the main audio output mix.
+ * <p>Creating a BassBoost on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
+ * controlling audio effects.
  */
 
 public class BassBoost extends AudioEffect {
@@ -55,7 +59,7 @@
     public static final int PARAM_STRENGTH_SUPPORTED = 0;
     /**
      * Bass boost effect strength. Parameter ID for
-     * {@link android.media.BassBoost.OnParameterChangeListener}
+     * {@link android.media.audiofx.BassBoost.OnParameterChangeListener}
      */
     public static final int PARAM_STRENGTH = 1;
 
@@ -155,7 +159,6 @@
          * BassBoost engine.
          * @param effect the BassBoost on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/EnvironmentalReverb.java b/media/java/android/media/audiofx/EnvironmentalReverb.java
similarity index 96%
rename from media/java/android/media/EnvironmentalReverb.java
rename to media/java/android/media/audiofx/EnvironmentalReverb.java
index b50febc..f1f582e 100644
--- a/media/java/android/media/EnvironmentalReverb.java
+++ b/media/java/android/media/audiofx/EnvironmentalReverb.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -28,18 +28,18 @@
 import java.util.StringTokenizer;
 
 /**
- * A sound generated within a room travels in many directions. The listener first hears the
- * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound
- * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after
- * undergoing more and more reflections, individual reflections become indistinguishable and
- * the listener hears continuous reverberation that decays over time.
+ * A sound generated within a room travels in many directions. The listener first hears the direct
+ * sound from the source itself. Later, he or she hears discrete echoes caused by sound bouncing off
+ * nearby walls, the ceiling and the floor. As sound waves arrive after undergoing more and more
+ * reflections, individual reflections become indistinguishable and the listener hears continuous
+ * reverberation that decays over time.
  * Reverb is vital for modeling a listener's environment. It can be used in music applications
  * to simulate music being played back in various environments, or in games to immerse the
  * listener within the game's environment.
  * The EnvironmentalReverb class allows an application to control each reverb engine property in a
  * global reverb environment and is more suitable for games. For basic control, more suitable for
  * music applications, it is recommended to use the
- * {@link android.media.PresetReverb} class.
+ * {@link android.media.audiofx.PresetReverb} class.
  * <p>An application creates a EnvironmentalReverb object to instantiate and control a reverb engine
  * in the audio framework.
  * <p>The methods, parameter types and units exposed by the EnvironmentalReverb implementation are
@@ -51,7 +51,9 @@
  * they must be explicitely attached to it and a send level must be specified. Use the effect ID
  * returned by getId() method to designate this particular effect when attaching it to the
  * MediaPlayer or AudioTrack.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling
+ * <p>Creating a reverb on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
  * audio effects.
  */
 
@@ -76,7 +78,7 @@
     public static final int PARAM_DECAY_TIME = 2;
     /**
      * Decay HF ratio. Parameter ID for
-     * {@link android.media.EnvironmentalReverb.OnParameterChangeListener}
+     * {@link android.media.audiofx.EnvironmentalReverb.OnParameterChangeListener}
      */
     public static final int PARAM_DECAY_HF_RATIO = 3;
     /**
@@ -444,7 +446,6 @@
          * EnvironmentalReverb engine.
          * @param effect the EnvironmentalReverb on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_ROOM_LEVEL} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/Equalizer.java b/media/java/android/media/audiofx/Equalizer.java
similarity index 97%
rename from media/java/android/media/Equalizer.java
rename to media/java/android/media/audiofx/Equalizer.java
index 6fa48c5..b3bafa9 100644
--- a/media/java/android/media/Equalizer.java
+++ b/media/java/android/media/audiofx/Equalizer.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -41,7 +41,11 @@
  * <p>To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session
  * ID of this AudioTrack or MediaPlayer when constructing the Equalizer. If the audio session ID 0
  * is specified, the Equalizer applies to the main audio output mix.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * <p>Creating an Equalizer on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
+ * effects.
  */
 
 public class Equalizer extends AudioEffect {
@@ -68,7 +72,7 @@
     public static final int PARAM_CENTER_FREQ = 3;
     /**
      * Band frequency range. Parameter ID for
-     * {@link android.media.Equalizer.OnParameterChangeListener}
+     * {@link android.media.audiofx.Equalizer.OnParameterChangeListener}
      */
     public static final int PARAM_BAND_FREQ_RANGE = 4;
     /**
@@ -380,7 +384,6 @@
          * Equalizer engine.
          * @param effect the Equalizer on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ...
          * @param param2 additional parameter qualifier (e.g the band for band level parameter).
          * @param value the new parameter value.
diff --git a/media/java/android/media/PresetReverb.java b/media/java/android/media/audiofx/PresetReverb.java
similarity index 96%
rename from media/java/android/media/PresetReverb.java
rename to media/java/android/media/audiofx/PresetReverb.java
index 65175ff..7a89ae7 100644
--- a/media/java/android/media/PresetReverb.java
+++ b/media/java/android/media/audiofx/PresetReverb.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -40,7 +40,7 @@
  * The PresetReverb class allows an application to configure the global reverb using a reverb preset.
  * This is primarily used for adding some reverb in a music playback context. Applications
  * requiring control over a more advanced environmental reverb are advised to use the
- * {@link android.media.EnvironmentalReverb} class.
+ * {@link android.media.audiofx.EnvironmentalReverb} class.
  * <p>An application creates a PresetReverb object to instantiate and control a reverb engine in the
  * audio framework.
  * <p>The methods, parameter types and units exposed by the PresetReverb implementation are
@@ -52,7 +52,10 @@
  * they must be explicitely attached to it and a send level must be specified. Use the effect ID
  * returned by getId() method to designate this particular effect when attaching it to the
  * MediaPlayer or AudioTrack.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * <p>Creating a reverb on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
+ * audio effects.
  */
 
 public class PresetReverb extends AudioEffect {
@@ -64,7 +67,7 @@
 
     /**
      * Preset. Parameter ID for
-     * {@link android.media.PresetReverb.OnParameterChangeListener}
+     * {@link android.media.audiofx.PresetReverb.OnParameterChangeListener}
      */
     public static final int PARAM_PRESET = 0;
 
@@ -174,7 +177,6 @@
          * PresetReverb engine.
          * @param effect the PresetReverb on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_PRESET} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java
similarity index 95%
rename from media/java/android/media/Virtualizer.java
rename to media/java/android/media/audiofx/Virtualizer.java
index b08f36e..a682a45 100644
--- a/media/java/android/media/Virtualizer.java
+++ b/media/java/android/media/audiofx/Virtualizer.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -42,7 +42,11 @@
  * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session
  * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer. If the audio session ID 0
  * is specified, the Virtualizer applies to the main audio output mix.
- * <p> See {@link android.media.AudioEffect} class for more details on controlling audio effects.
+ * <p>Creating a Virtualizer on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
+ * audio effects.
  */
 
 public class Virtualizer extends AudioEffect {
@@ -56,7 +60,7 @@
     public static final int PARAM_STRENGTH_SUPPORTED = 0;
     /**
      * Virtualizer effect strength. Parameter ID for
-     * {@link android.media.Virtualizer.OnParameterChangeListener}
+     * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
      */
     public static final int PARAM_STRENGTH = 1;
 
@@ -156,7 +160,6 @@
          * Virtualizer engine.
          * @param effect the Virtualizer on which the interface is registered.
          * @param status status of the set parameter operation.
-         * See {@link android.media.AudioEffect#setParameter(byte[], byte[])}.
          * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
          * @param value the new parameter value.
          */
diff --git a/media/java/android/media/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
similarity index 96%
rename from media/java/android/media/Visualizer.java
rename to media/java/android/media/audiofx/Visualizer.java
index 453fc04..0c48556 100755
--- a/media/java/android/media/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.media.audiofx;
 
 import android.util.Log;
 import java.lang.ref.WeakReference;
@@ -32,8 +32,8 @@
  * visualized:<br>
  * <ul>
  *   <li>If the session is 0, the audio output mix is visualized</li>
- *   <li>If the session is not 0, the audio from a particular {@link MediaPlayer} or
- *   {@link AudioTrack}
+ *   <li>If the session is not 0, the audio from a particular {@link android.media.MediaPlayer} or
+ *   {@link android.media.AudioTrack}
  *   using this audio session is visualized </li>
  * </ul>
  * <p>Two types of representation of audio content can be captured: <br>
@@ -57,6 +57,8 @@
  * When data capture is not needed any more, the Visualizer should be disabled.
  * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used
  * anymore to free up native resources associated to the Visualizer instance.
+ * <p>Creating a Visualizer on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
  */
 
 public class Visualizer {
@@ -218,13 +220,16 @@
     public int setEnabled(boolean enabled)
     throws IllegalStateException {
         synchronized (mStateLock) {
-            if ((enabled && mState != STATE_INITIALIZED) ||
-                    (!enabled && mState != STATE_ENABLED)) {
+            if (mState == STATE_UNINITIALIZED) {
                 throw(new IllegalStateException("setEnabled() called in wrong state: "+mState));
             }
-            int status = native_setEnabled(enabled);
-            if (status == SUCCESS) {
-                mState = enabled ? STATE_ENABLED : STATE_INITIALIZED;
+            int status = SUCCESS;
+            if ((enabled && (mState == STATE_INITIALIZED)) ||
+                    (!enabled && (mState == STATE_ENABLED))) {
+                status = native_setEnabled(enabled);
+                if (status == SUCCESS) {
+                    mState = enabled ? STATE_ENABLED : STATE_INITIALIZED;
+                }
             }
             return status;
         }
diff --git a/media/java/android/media/audiofx/package.html b/media/java/android/media/audiofx/package.html
new file mode 100644
index 0000000..c6e4892
--- /dev/null
+++ b/media/java/android/media/audiofx/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Provides classes that manage audio effects implemented in the media framework.
+</BODY>
+</HTML>
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 8d9f4fe..cb2f0f9 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -37,7 +37,7 @@
 #define AUDIOEFFECT_ERROR_DEAD_OBJECT           -7
 
 // ----------------------------------------------------------------------------
-static const char* const kClassPathName = "android/media/AudioEffect";
+static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
 
 struct fields_t {
     // these fields provide access from C++ to the...
@@ -228,9 +228,9 @@
         return;
     }
 
-    clazz = env->FindClass("android/media/AudioEffect$Descriptor");
+    clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
     if (clazz == NULL) {
-        LOGE("Can't find android/media/AudioEffect$Descriptor class");
+        LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
         return;
     }
     fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
@@ -241,7 +241,7 @@
                     "<init>",
                     "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     if (fields.midDescCstor == NULL) {
-        LOGE("Can't find android/media/AudioEffect$Descriptor class constructor");
+        LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
         return;
     }
 }
@@ -304,14 +304,7 @@
             lpJniStorage->mCallbackData.audioEffect_class,
             &lpJniStorage->mCallbackData);
 
-    if (jId) {
-        nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
-        if (nId == NULL) {
-            LOGE("setup: Error retrieving id pointer");
-            lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
-            goto setup_failure;
-        }
-    } else {
+    if (jId == NULL) {
         LOGE("setup: NULL java array for id pointer");
         lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
         goto setup_failure;
@@ -336,8 +329,13 @@
         goto setup_failure;
     }
 
+    nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
+    if (nId == NULL) {
+        LOGE("setup: Error retrieving id pointer");
+        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
+        goto setup_failure;
+    }
     nId[0] = lpAudioEffect->id();
-
     env->ReleasePrimitiveArrayCritical(jId, nId, 0);
     nId = NULL;
 
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 31119f8..57cafd4 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -40,7 +40,7 @@
 #define NATIVE_EVENT_FFT_CAPTURE                1
 
 // ----------------------------------------------------------------------------
-static const char* const kClassPathName = "android/media/Visualizer";
+static const char* const kClassPathName = "android/media/audiofx/Visualizer";
 
 struct fields_t {
     // these fields provide access from C++ to the...
@@ -246,14 +246,7 @@
             lpJniStorage->mCallbackData.visualizer_class,
             &lpJniStorage->mCallbackData);
 
-    if (jId) {
-        nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
-        if (nId == NULL) {
-            LOGE("setup: Error retrieving id pointer");
-            lStatus = VISUALIZER_ERROR_BAD_VALUE;
-            goto setup_failure;
-        }
-    } else {
+    if (jId == NULL) {
         LOGE("setup: NULL java array for id pointer");
         lStatus = VISUALIZER_ERROR_BAD_VALUE;
         goto setup_failure;
@@ -275,8 +268,13 @@
         goto setup_failure;
     }
 
+    nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
+    if (nId == NULL) {
+        LOGE("setup: Error retrieving id pointer");
+        lStatus = VISUALIZER_ERROR_BAD_VALUE;
+        goto setup_failure;
+    }
     nId[0] = lpVisualizer->id();
-
     env->ReleasePrimitiveArrayCritical(jId, nId, 0);
     nId = NULL;
 
@@ -424,7 +422,6 @@
     jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
 
     env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
-
     return status;
 }
 
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 8ab57c93..5505f14 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -65,8 +65,8 @@
 {
     pContext->mCaptureIdx = 0;
     pContext->mCurrentBuf = 0;
-    memset(pContext->mCaptureBuf[0], 0, VISUALIZER_CAPTURE_SIZE_MAX);
-    memset(pContext->mCaptureBuf[1], 0, VISUALIZER_CAPTURE_SIZE_MAX);
+    memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
+    memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
 }
 
 //----------------------------------------------------------------------------
@@ -239,6 +239,24 @@
     }
 
     // all code below assumes stereo 16 bit PCM output and input
+
+    // derive capture scaling factor from peak value in current buffer
+    // this gives more interesting captures for display.
+    int32_t shift = 32;
+    for (size_t i = 0; i < inBuffer->frameCount; i++) {
+        int32_t smp = inBuffer->s16[i];
+        if (smp < 0) smp = -smp;
+        int32_t clz = __builtin_clz(smp);
+        if (shift > clz) shift = clz;
+    }
+    // never scale by less than 8 to avoid returning unaltered PCM signal.
+    // add one to combine the division by 2 needed after summing left and right channels below
+    if (20 > shift) {
+        shift = (31 - 8 + 1) - shift;
+    } else {
+        shift = (3 + 1);
+    }
+
     uint32_t captIdx;
     uint32_t inIdx;
     uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
@@ -246,7 +264,7 @@
          inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
          inIdx++, captIdx++) {
         int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
-        smp = (smp + (1 << 8)) >> 9;
+        smp = (smp + (1 << (shift - 1))) >> shift;
         buf[captIdx] = ((uint8_t)smp)^0x80;
     }
     pContext->mCaptureIdx = captIdx;
@@ -369,7 +387,7 @@
 
 
     case VISU_CMD_CAPTURE:
-        if (pReplyData == NULL || *replySize != (int)pContext->mCaptureSize) {
+        if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
             LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
                     *replySize, pContext->mCaptureSize);
             return -EINVAL;
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 0f3e245..88b8c86 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -228,24 +228,32 @@
                               void *replyData)
 {
     if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
+        LOGV("command() bad status %d", mStatus);
         return INVALID_OPERATION;
     }
 
+    if ((cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) &&
+            (replySize == NULL || *replySize != sizeof(status_t) || replyData == NULL)) {
+        return BAD_VALUE;
+    }
+
     status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
     if (status != NO_ERROR) {
         return status;
     }
-    status = *(status_t *)replyData;
-    if (status != NO_ERROR) {
-        return status;
+
+    if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) {
+        status = *(status_t *)replyData;
+        if (status != NO_ERROR) {
+            return status;
+        }
+        if (cmdCode == EFFECT_CMD_ENABLE) {
+            android_atomic_or(1, &mEnabled);
+        } else {
+            android_atomic_and(~1, &mEnabled);
+        }
     }
 
-    if (cmdCode == EFFECT_CMD_ENABLE) {
-        android_atomic_or(1, &mEnabled);
-    }
-    if (cmdCode == EFFECT_CMD_DISABLE) {
-        android_atomic_and(~1, &mEnabled);
-    }
     return status;
 }
 
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 39552b6..68f2e9b 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -169,11 +169,13 @@
     status_t status = NO_ERROR;
     if (mEnabled) {
         uint32_t replySize = mCaptureSize;
-        status_t status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
+        status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
+        LOGV("getWaveForm() command returned %d", status);
         if (replySize == 0) {
             status = NOT_ENOUGH_DATA;
         }
     } else {
+        LOGV("getWaveForm() disabled");
         memset(waveform, 0x80, mCaptureSize);
     }
     return status;
@@ -191,7 +193,7 @@
     status_t status = NO_ERROR;
     if (mEnabled) {
         uint8_t buf[mCaptureSize];
-        status_t status = getWaveForm(buf);
+        status = getWaveForm(buf);
         if (status == NO_ERROR) {
             status = doFft(fft, buf);
         }
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index aba5f52..d975cb9 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 e0321a5..3e17a7e 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -16,6 +16,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..47a385d 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;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8507afc..12022bd 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -639,7 +639,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
@@ -666,8 +666,6 @@
         } else {
             mAudioPlayer->resume();
         }
-
-        postCheckAudioStatusEvent_l();
     }
 
     if (mTimeSource == NULL && mAudioPlayer == NULL) {
@@ -1169,7 +1167,7 @@
         return;
     }
     mAudioStatusEventPending = true;
-    mQueue.postEventWithDelay(mCheckAudioStatusEvent, 100000ll);
+    mQueue.postEvent(mCheckAudioStatusEvent);
 }
 
 void AwesomePlayer::onCheckAudioStatus() {
@@ -1200,8 +1198,6 @@
         mFlags |= FIRST_FRAME;
         postStreamDoneEvent_l(finalStatus);
     }
-
-    postCheckAudioStatusEvent_l();
 }
 
 status_t AwesomePlayer::prepare() {
@@ -1662,5 +1658,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/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f0d8943..e6c2f7e 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1541,20 +1541,52 @@
         int32_t isSync = false;
         meta_data->findInt32(kKeyIsSyncFrame, &isSync);
 
+        /*
+         * The original timestamp found in the data buffer will be modified as below:
+         *
+         * There is a playback offset into this track if the track's start time
+         * is not the same as the movie start time, which will be recorded in edst
+         * box of the output file. The playback offset is to make sure that the
+         * starting time of the audio/video tracks are synchronized. Although the
+         * track's media timestamp may be subject to various modifications
+         * as outlined below, the track's playback offset time remains unchanged
+         * once the first data buffer of the track is received.
+         *
+         * The media time stamp will be calculated by subtracting the playback offset
+         * (and potential pause durations) from the original timestamp in the buffer.
+         *
+         * If this track is a video track for a real-time recording application with
+         * both audio and video tracks, its media timestamp will subject to further
+         * modification based on the media clock of the audio track. This modification
+         * is needed for the purpose of maintaining good audio/video synchronization.
+         *
+         * If the recording session is paused and resumed multiple times, the track
+         * media timestamp will be modified as if the  recording session had never been
+         * paused at all during playback of the recorded output file. In other words,
+         * the output file will have no memory of pause/resume durations.
+         *
+         */
         CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
+        LOGV("%s timestampUs: %lld", mIsAudio? "Audio": "Video", timestampUs);
 
 ////////////////////////////////////////////////////////////////////////////////
         if (mSampleSizes.empty()) {
             mStartTimestampUs = timestampUs;
             mOwner->setStartTimestampUs(mStartTimestampUs);
+            previousPausedDurationUs = mStartTimestampUs;
         }
 
         if (mResumed) {
-            previousPausedDurationUs += (timestampUs - mTrackDurationUs - lastDurationUs);
+            int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
+            CHECK(durExcludingEarlierPausesUs >= 0);
+            int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
+            CHECK(pausedDurationUs >= lastDurationUs);
+            previousPausedDurationUs += pausedDurationUs - lastDurationUs;
             mResumed = false;
         }
 
         timestampUs -= previousPausedDurationUs;
+        CHECK(timestampUs >= 0);
         if (mIsRealTimeRecording && !mIsAudio) {
             // The minor adjustment on the timestamp is heuristic/experimental
             // We are adjusting the timestamp to reduce the fluctuation of the duration
@@ -1590,8 +1622,8 @@
             }
         }
 
-        LOGV("time stamp: %lld and previous paused duration %lld",
-                timestampUs, previousPausedDurationUs);
+        LOGV("%s media time stamp: %lld and previous paused duration %lld",
+                mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
         if (timestampUs > mTrackDurationUs) {
             mTrackDurationUs = timestampUs;
         }
@@ -1873,6 +1905,7 @@
 
             // First elst entry: specify the starting time offset
             int64_t offsetUs = mStartTimestampUs - moovStartTimeUs;
+            LOGV("OffsetUs: %lld", offsetUs);
             int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6;
             mOwner->writeInt32(seg);         // in mvhd timecale
             mOwner->writeInt32(-1);          // starting time offset
@@ -2111,7 +2144,15 @@
                       mOwner->endBox();  // d263
                   } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
                       CHECK(mCodecSpecificData);
-                      CHECK(mCodecSpecificDataSize > 0);
+                      CHECK(mCodecSpecificDataSize >= 5);
+
+                      // Patch avcc's lengthSize field to match the number
+                      // of bytes we use to indicate the size of a nal unit.
+                      uint8_t *ptr = (uint8_t *)mCodecSpecificData;
+                      ptr[4] =
+                          (ptr[4] & 0xfc)
+                            | (mOwner->useNalLengthFour() ? 3 : 1);
+
                       mOwner->beginBox("avcC");
                         mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
                       mOwner->endBox();  // avcC
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 9171aab..3a0fc41 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -132,7 +132,7 @@
 }
 
 void PageCache::copy(size_t from, void *data, size_t size) {
-    LOG(VERBOSE) << "copy from " << from << " size " << size;
+    LOGV("copy from %d size %d", from, size);
 
     CHECK_LE(from + size, mTotalSize);
 
@@ -228,7 +228,7 @@
 }
 
 void NuCachedSource2::fetchInternal() {
-    LOG(VERBOSE) << "fetchInternal";
+    LOGV("fetchInternal");
 
     CHECK_EQ(mFinalStatus, (status_t)OK);
 
@@ -240,11 +240,11 @@
     Mutex::Autolock autoLock(mLock);
 
     if (n < 0) {
-        LOG(ERROR) << "source returned error " << n;
+        LOGE("source returned error %ld", n);
         mFinalStatus = n;
         mCache->releasePage(page);
     } else if (n == 0) {
-        LOG(INFO) << "ERROR_END_OF_STREAM";
+        LOGI("ERROR_END_OF_STREAM");
         mFinalStatus = ERROR_END_OF_STREAM;
         mCache->releasePage(page);
     } else {
@@ -254,10 +254,10 @@
 }
 
 void NuCachedSource2::onFetch() {
-    LOG(VERBOSE) << "onFetch";
+    LOGV("onFetch");
 
     if (mFinalStatus != OK) {
-        LOG(VERBOSE) << "EOS reached, done prefetching for now";
+        LOGV("EOS reached, done prefetching for now");
         mFetching = false;
     }
 
@@ -268,7 +268,7 @@
 
     if (mFetching || keepAlive) {
         if (keepAlive) {
-            LOG(INFO) << "Keep alive";
+            LOGI("Keep alive");
         }
 
         fetchInternal();
@@ -276,7 +276,7 @@
         mLastFetchTimeUs = ALooper::GetNowUs();
 
         if (mFetching && mCache->totalSize() >= kHighWaterThreshold) {
-            LOG(INFO) << "Cache full, done prefetching for now";
+            LOGI("Cache full, done prefetching for now");
             mFetching = false;
         }
     } else {
@@ -289,7 +289,7 @@
 }
 
 void NuCachedSource2::onRead(const sp<AMessage> &msg) {
-    LOG(VERBOSE) << "onRead";
+    LOGV("onRead");
 
     int64_t offset;
     CHECK(msg->findInt64("offset", &offset));
@@ -339,14 +339,14 @@
     size_t actualBytes = mCache->releaseFromStart(maxBytes);
     mCacheOffset += actualBytes;
 
-    LOG(INFO) << "restarting prefetcher, totalSize = " << mCache->totalSize();
+    LOGI("restarting prefetcher, totalSize = %d", mCache->totalSize());
     mFetching = true;
 }
 
 ssize_t NuCachedSource2::readAt(off_t offset, void *data, size_t size) {
     Mutex::Autolock autoSerializer(mSerializer);
 
-    LOG(VERBOSE) << "readAt offset " << offset << " size " << size;
+    LOGV("readAt offset %ld, size %d", offset, size);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -406,7 +406,7 @@
 }
 
 ssize_t NuCachedSource2::readInternal(off_t offset, void *data, size_t size) {
-    LOG(VERBOSE) << "readInternal offset " << offset << " size " << size;
+    LOGV("readInternal offset %ld size %d", offset, size);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -442,7 +442,7 @@
         return size;
     }
 
-    LOG(VERBOSE) << "deferring read";
+    LOGV("deferring read");
 
     return -EAGAIN;
 }
@@ -455,7 +455,7 @@
         return OK;
     }
 
-    LOG(INFO) << "new range: offset= " << offset;
+    LOGI("new range: offset= %ld", offset);
 
     mCacheOffset = offset;
 
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index ab9285d..fcbfdac 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -96,6 +96,11 @@
     return connect(host, port, path, headers, offset);
 }
 
+static bool IsRedirectStatusCode(int httpStatus) {
+    return httpStatus == 301 || httpStatus == 302
+        || httpStatus == 303 || httpStatus == 307;
+}
+
 status_t NuHTTPDataSource::connect(
         const char *host, unsigned port, const char *path,
         const String8 &headers,
@@ -161,7 +166,7 @@
             return err;
         }
 
-        if (httpStatus == 302) {
+        if (IsRedirectStatusCode(httpStatus)) {
             string value;
             CHECK(mHTTP.find_header_value("Location", &value));
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 523b79c..76c8870 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -980,7 +980,10 @@
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamVideoErrorCorrection,
             &errorCorrectionType, sizeof(errorCorrectionType));
-    CHECK_EQ(err, OK);
+    if (err != OK) {
+        LOGW("Error correction param query is not supported");
+        return OK;  // Optional feature. Ignore this failure
+    }
 
     errorCorrectionType.bEnableHEC = OMX_FALSE;
     errorCorrectionType.bEnableResync = OMX_TRUE;
@@ -991,7 +994,11 @@
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoErrorCorrection,
             &errorCorrectionType, sizeof(errorCorrectionType));
-    CHECK_EQ(err, OK);
+    if (err != OK) {
+        LOGW("Error correction param configuration is not supported");
+    }
+
+    // Optional feature. Ignore the failure.
     return OK;
 }
 
@@ -1089,8 +1096,8 @@
 
     // Check profile and level parameters
     CodecProfileLevel defaultProfileLevel, profileLevel;
-    defaultProfileLevel.mProfile = OMX_VIDEO_H263ProfileBaseline;
-    defaultProfileLevel.mLevel = OMX_VIDEO_H263Level45;
+    defaultProfileLevel.mProfile = h263type.eProfile;
+    defaultProfileLevel.mLevel = h263type.eLevel;
     err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
     if (err != OK) return err;
     h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profileLevel.mProfile);
@@ -1146,8 +1153,8 @@
 
     // Check profile and level parameters
     CodecProfileLevel defaultProfileLevel, profileLevel;
-    defaultProfileLevel.mProfile = OMX_VIDEO_MPEG4ProfileSimple;
-    defaultProfileLevel.mLevel = OMX_VIDEO_MPEG4Level2;
+    defaultProfileLevel.mProfile = mpeg4type.eProfile;
+    defaultProfileLevel.mLevel = mpeg4type.eLevel;
     err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
     if (err != OK) return err;
     mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profileLevel.mProfile);
@@ -1798,7 +1805,7 @@
 
         case OMX_EventError:
         {
-            LOGE("ERROR(0x%08lx, %ld)", data1, data2);
+            CODEC_LOGE("ERROR(0x%08lx, %ld)", data1, data2);
 
             setState(ERROR);
             break;
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/ThreadedSource.cpp b/media/libstagefright/ThreadedSource.cpp
index 5add2a5..38c6e2d 100644
--- a/media/libstagefright/ThreadedSource.cpp
+++ b/media/libstagefright/ThreadedSource.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "ThreadedSource"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
 #include "include/ThreadedSource.h"
 
 #include <media/stagefright/foundation/ADebug.h>
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index c5b51c0..e4ed5e6 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -123,6 +123,8 @@
     mAnchorTimeUs = 0;
     mNumSamplesOutput = 0;
     mStarted = true;
+    mNumDecodedBuffers = 0;
+    mUpsamplingFactor = 2;
 
     return OK;
 }
@@ -207,22 +209,65 @@
 
     Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
 
-    // Check on the sampling rate to see whether it is changed.
-    int32_t sampleRate;
-    CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate));
-    if (mConfig->samplingRate != sampleRate) {
-        mMeta->setInt32(kKeySampleRate, mConfig->samplingRate);
-        LOGW("Sample rate was %d, but now is %d",
-                sampleRate, mConfig->samplingRate);
-        buffer->release();
-        mInputBuffer->release();
-        mInputBuffer = NULL;
-        return INFO_FORMAT_CHANGED;
+    /*
+     * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+     * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+     * rate system and the sampling rate in the final output is actually
+     * doubled compared with the core AAC decoder sampling rate.
+     *
+     * Explicit signalling is done by explicitly defining SBR audio object
+     * type in the bitstream. Implicit signalling is done by embedding
+     * SBR content in AAC extension payload specific to SBR, and hence
+     * requires an AAC decoder to perform pre-checks on actual audio frames.
+     *
+     * Thus, we could not say for sure whether a stream is
+     * AAC+/eAAC+ until the first data frame is decoded.
+     */
+    if (++mNumDecodedBuffers <= 2) {
+        LOGV("audio/extended audio object type: %d + %d",
+            mConfig->audioObjectType, mConfig->extendedAudioObjectType);
+        LOGV("aac+ upsampling factor: %d desired channels: %d",
+            mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels);
+
+        CHECK(mNumDecodedBuffers > 0);
+        if (mNumDecodedBuffers == 1) {
+            mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor;
+            // Check on the sampling rate to see whether it is changed.
+            int32_t sampleRate;
+            CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate));
+            if (mConfig->samplingRate != sampleRate) {
+                mMeta->setInt32(kKeySampleRate, mConfig->samplingRate);
+                LOGW("Sample rate was %d Hz, but now is %d Hz",
+                        sampleRate, mConfig->samplingRate);
+                buffer->release();
+                mInputBuffer->release();
+                mInputBuffer = NULL;
+                return INFO_FORMAT_CHANGED;
+            }
+        } else {  // mNumDecodedBuffers == 2
+            if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC ||
+                mConfig->extendedAudioObjectType == MP4AUDIO_LTP) {
+                if (mUpsamplingFactor == 2) {
+                    // The stream turns out to be not aacPlus mode anyway
+                    LOGW("Disable AAC+/eAAC+ since extended audio object type is %d",
+                        mConfig->extendedAudioObjectType);
+                    mConfig->aacPlusEnabled = 0;
+                }
+            } else {
+                if (mUpsamplingFactor == 1) {
+                    // aacPlus mode does not buy us anything, but to cause
+                    // 1. CPU load to increase, and
+                    // 2. a half speed of decoding
+                    LOGW("Disable AAC+/eAAC+ since upsampling factor is 1");
+                    mConfig->aacPlusEnabled = 0;
+                }
+            }
+        }
     }
 
     size_t numOutBytes =
         mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels;
-    if (mConfig->aacPlusUpsamplingFactor == 2) {
+    if (mUpsamplingFactor == 2) {
         if (mConfig->desiredChannels == 1) {
             memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2);
         }
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 7483d60..3c0b736 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -232,6 +232,53 @@
     }
 }
 
+MediaBuffer *AVCDecoder::drainOutputBuffer() {
+    int32_t index;
+    int32_t Release;
+    AVCFrameIO Output;
+    Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
+    AVCDec_Status status = PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
+
+    if (status != AVCDEC_SUCCESS) {
+        LOGV("PVAVCDecGetOutput returned error %d", status);
+        return NULL;
+    }
+
+    CHECK(index >= 0);
+    CHECK(index < (int32_t)mFrames.size());
+
+    MediaBuffer *mbuf = mFrames.editItemAt(index);
+
+    bool skipFrame = false;
+
+    if (mTargetTimeUs >= 0) {
+        int64_t timeUs;
+        CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
+        CHECK(timeUs <= mTargetTimeUs);
+
+        if (timeUs < mTargetTimeUs) {
+            // We're still waiting for the frame with the matching
+            // timestamp and we won't return the current one.
+            skipFrame = true;
+
+            LOGV("skipping frame at %lld us", timeUs);
+        } else {
+            LOGV("found target frame at %lld us", timeUs);
+
+            mTargetTimeUs = -1;
+        }
+    }
+
+    if (!skipFrame) {
+        mbuf->set_range(0, mbuf->size());
+        mbuf->add_ref();
+
+        return mbuf;
+    }
+
+    return new MediaBuffer(0);
+}
+
 status_t AVCDecoder::read(
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
@@ -279,7 +326,8 @@
                 seekOptions.clearSeekTo();
 
                 if (err != OK) {
-                    return err;
+                    *out = drainOutputBuffer();
+                    return (*out == NULL)  ? err : (status_t)OK;
                 }
 
                 if (mInputBuffer->range_length() > 0) {
@@ -415,51 +463,12 @@
                         fragSize);
 
                 if (res == AVCDEC_PICTURE_OUTPUT_READY) {
-                    int32_t index;
-                    int32_t Release;
-                    AVCFrameIO Output;
-                    Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
-
-                    AVCDec_Status status =
-                        PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
-
-                    if (status != AVCDEC_SUCCESS) {
-                        LOGV("PVAVCDecGetOutput returned error %d", status);
+                    MediaBuffer *mbuf = drainOutputBuffer();
+                    if (mbuf == NULL) {
                         break;
                     }
 
-                    CHECK(index >= 0);
-                    CHECK(index < (int32_t)mFrames.size());
-
-                    MediaBuffer *mbuf = mFrames.editItemAt(index);
-
-                    bool skipFrame = false;
-
-                    if (mTargetTimeUs >= 0) {
-                        int64_t timeUs;
-                        CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
-                        CHECK(timeUs <= mTargetTimeUs);
-
-                        if (timeUs < mTargetTimeUs) {
-                            // We're still waiting for the frame with the matching
-                            // timestamp and we won't return the current one.
-                            skipFrame = true;
-
-                            LOGV("skipping frame at %lld us", timeUs);
-                        } else {
-                            LOGV("found target frame at %lld us", timeUs);
-
-                            mTargetTimeUs = -1;
-                        }
-                    }
-
-                    if (!skipFrame) {
-                        *out = mbuf;
-                        (*out)->set_range(0, (*out)->size());
-                        (*out)->add_ref();
-                    } else {
-                        *out = new MediaBuffer(0);
-                    }
+                    *out = mbuf;
 
                     // Do _not_ release input buffer yet.
 
@@ -496,6 +505,7 @@
 
             case AVC_NALTYPE_AUD:
             case AVC_NALTYPE_FILL:
+            case AVC_NALTYPE_EOSEQ:
             {
                 *out = new MediaBuffer(0);
 
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/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp
deleted file mode 100644
index 16f8b22..0000000
--- a/media/libstagefright/foundation/ADebug.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ADebug.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef ANDROID
-#include <cutils/log.h>
-#endif
-
-namespace android {
-
-Logger::Logger(LogType type)
-    : mLogType(type) {
-    switch (mLogType) {
-        case VERBOSE:
-            mMessage = "V ";
-            break;
-        case INFO:
-            mMessage = "I ";
-            break;
-        case WARNING:
-            mMessage = "W ";
-            break;
-        case ERROR:
-            mMessage = "E ";
-            break;
-        case FATAL:
-            mMessage = "F ";
-            break;
-
-        default:
-            break;
-    }
-}
-
-Logger::~Logger() {
-    if (mLogType == VERBOSE) {
-        return;
-    }
-
-    mMessage.append("\n");
-
-#if defined(ANDROID) && 1
-    LOG_PRI(ANDROID_LOG_INFO, "ADebug", "%s", mMessage.c_str());
-#else
-    fprintf(stderr, mMessage.c_str());
-    fflush(stderr);
-#endif
-
-    if (mLogType == FATAL) {
-        abort();
-    }
-}
-
-const char *LeafName(const char *s) {
-    const char *lastSlash = strrchr(s, '/');
-    return lastSlash != NULL ? lastSlash + 1 : s;
-}
-
-}  // namespace android
diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp
index 65f7593..7683113 100644
--- a/media/libstagefright/foundation/ALooperRoster.cpp
+++ b/media/libstagefright/foundation/ALooperRoster.cpp
@@ -74,7 +74,7 @@
     ssize_t index = mHandlers.indexOfKey(msg->target());
 
     if (index < 0) {
-        LOG(WARNING) << "failed to post message. Target handler not registered.";
+        LOGW("failed to post message. Target handler not registered.");
         return;
     }
 
@@ -83,8 +83,8 @@
     sp<ALooper> looper = info.mLooper.promote();
 
     if (looper == NULL) {
-        LOG(WARNING) << "failed to post message. "
-                        "Target handler still registered, but object gone.";
+        LOGW("failed to post message. "
+             "Target handler still registered, but object gone.");
 
         mHandlers.removeItemsAt(index);
         return;
@@ -102,8 +102,7 @@
         ssize_t index = mHandlers.indexOfKey(msg->target());
 
         if (index < 0) {
-            LOG(WARNING) << "failed to deliver message. "
-                         << "Target handler not registered.";
+            LOGW("failed to deliver message. Target handler not registered.");
             return;
         }
 
@@ -111,8 +110,8 @@
         handler = info.mHandler.promote();
 
         if (handler == NULL) {
-            LOG(WARNING) << "failed to deliver message. "
-                            "Target handler registered, but object gone.";
+            LOGW("failed to deliver message. "
+                 "Target handler registered, but object gone.");
 
             mHandlers.removeItemsAt(index);
             return;
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index f6a8a52..ffa7db0 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -5,7 +5,6 @@
     AAtomizer.cpp               \
     ABitReader.cpp              \
     ABuffer.cpp                 \
-    ADebug.cpp                  \
     AHandler.cpp                \
     ALooper.cpp                 \
     ALooperRoster.cpp           \
diff --git a/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/foundation/hexdump.cpp
index 093b587..9f6bf9e 100644
--- a/media/libstagefright/foundation/hexdump.cpp
+++ b/media/libstagefright/foundation/hexdump.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "hexdump"
+#include <utils/Log.h>
+
 #include "hexdump.h"
 
 #include "ADebug.h"
@@ -63,7 +67,7 @@
             }
         }
 
-        LOG(INFO) << line;
+        LOGI("%s", line.c_str());
 
         offset += 16;
     }
diff --git a/media/libstagefright/include/AACDecoder.h b/media/libstagefright/include/AACDecoder.h
index 200f93c..886a3b7 100644
--- a/media/libstagefright/include/AACDecoder.h
+++ b/media/libstagefright/include/AACDecoder.h
@@ -53,6 +53,8 @@
     int64_t mAnchorTimeUs;
     int64_t mNumSamplesOutput;
     status_t mInitCheck;
+    int64_t  mNumDecodedBuffers;
+    int32_t  mUpsamplingFactor;
 
     MediaBuffer *mInputBuffer;
 
diff --git a/media/libstagefright/include/AVCDecoder.h b/media/libstagefright/include/AVCDecoder.h
index 898c90a..eb3b142 100644
--- a/media/libstagefright/include/AVCDecoder.h
+++ b/media/libstagefright/include/AVCDecoder.h
@@ -82,6 +82,8 @@
 
     void releaseFrames();
 
+    MediaBuffer *drainOutputBuffer();
+
     AVCDecoder(const AVCDecoder &);
     AVCDecoder &operator=(const AVCDecoder &);
 };
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 1f3946c..600faca 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -92,6 +92,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 72de8d7..47cca80 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ATSParser"
+#include <utils/Log.h>
+
 #include "ATSParser.h"
 
 #include "AnotherPacketSource.h"
@@ -31,6 +35,10 @@
 
 namespace android {
 
+// I want the expression "y" evaluated even if verbose logging is off.
+#define MY_LOGV(x, y) \
+    do { unsigned tmp = y; LOGV(x, tmp); } while (0)
+
 static const size_t kTSPacketSize = 188;
 
 struct ATSParser::Program : public RefBase {
@@ -115,36 +123,33 @@
 
 void ATSParser::Program::parseProgramMap(ABitReader *br) {
     unsigned table_id = br->getBits(8);
-    LOG(VERBOSE) << "  table_id = " << table_id;
+    LOGV("  table_id = %u", table_id);
     CHECK_EQ(table_id, 0x02u);
 
-    unsigned section_syntax_indictor = br->getBits(1);
-    LOG(VERBOSE) << "  section_syntax_indictor = " << section_syntax_indictor;
-    CHECK_EQ(section_syntax_indictor, 1u);
+    unsigned section_syntax_indicator = br->getBits(1);
+    LOGV("  section_syntax_indicator = %u", section_syntax_indicator);
+    CHECK_EQ(section_syntax_indicator, 1u);
 
     CHECK_EQ(br->getBits(1), 0u);
-    LOG(VERBOSE) << "  reserved = " << br->getBits(2);
+    MY_LOGV("  reserved = %u", br->getBits(2));
 
     unsigned section_length = br->getBits(12);
-    LOG(VERBOSE) << "  section_length = " << section_length;
+    LOGV("  section_length = %u", section_length);
     CHECK((section_length & 0xc00) == 0);
     CHECK_LE(section_length, 1021u);
 
-    LOG(VERBOSE) << "  program_number = " << br->getBits(16);
-    LOG(VERBOSE) << "  reserved = " << br->getBits(2);
-    LOG(VERBOSE) << "  version_number = " << br->getBits(5);
-    LOG(VERBOSE) << "  current_next_indicator = " << br->getBits(1);
-    LOG(VERBOSE) << "  section_number = " << br->getBits(8);
-    LOG(VERBOSE) << "  last_section_number = " << br->getBits(8);
-    LOG(VERBOSE) << "  reserved = " << br->getBits(3);
-
-    LOG(VERBOSE) << "  PCR_PID = "
-              << StringPrintf("0x%04x", br->getBits(13));
-
-    LOG(VERBOSE) << "  reserved = " << br->getBits(4);
+    MY_LOGV("  program_number = %u", br->getBits(16));
+    MY_LOGV("  reserved = %u", br->getBits(2));
+    MY_LOGV("  version_number = %u", br->getBits(5));
+    MY_LOGV("  current_next_indicator = %u", br->getBits(1));
+    MY_LOGV("  section_number = %u", br->getBits(8));
+    MY_LOGV("  last_section_number = %u", br->getBits(8));
+    MY_LOGV("  reserved = %u", br->getBits(3));
+    MY_LOGV("  PCR_PID = 0x%04x", br->getBits(13));
+    MY_LOGV("  reserved = %u", br->getBits(4));
 
     unsigned program_info_length = br->getBits(12);
-    LOG(VERBOSE) << "  program_info_length = " << program_info_length;
+    LOGV("  program_info_length = %u", program_info_length);
     CHECK((program_info_length & 0xc00) == 0);
 
     br->skipBits(program_info_length * 8);  // skip descriptors
@@ -158,19 +163,17 @@
         CHECK_GE(infoBytesRemaining, 5u);
 
         unsigned streamType = br->getBits(8);
-        LOG(VERBOSE) << "    stream_type = "
-                  << StringPrintf("0x%02x", streamType);
+        LOGV("    stream_type = 0x%02x", streamType);
 
-        LOG(VERBOSE) << "    reserved = " << br->getBits(3);
+        MY_LOGV("    reserved = %u", br->getBits(3));
 
         unsigned elementaryPID = br->getBits(13);
-        LOG(VERBOSE) << "    elementary_PID = "
-                  << StringPrintf("0x%04x", elementaryPID);
+        LOGV("    elementary_PID = 0x%04x", elementaryPID);
 
-        LOG(VERBOSE) << "    reserved = " << br->getBits(4);
+        MY_LOGV("    reserved = %u", br->getBits(4));
 
         unsigned ES_info_length = br->getBits(12);
-        LOG(VERBOSE) << "    ES_info_length = " << ES_info_length;
+        LOGV("    ES_info_length = %u", ES_info_length);
         CHECK((ES_info_length & 0xc00) == 0);
 
         CHECK_GE(infoBytesRemaining - 5, ES_info_length);
@@ -180,10 +183,10 @@
 #else
         unsigned info_bytes_remaining = ES_info_length;
         while (info_bytes_remaining >= 2) {
-            LOG(VERBOSE) << "      tag = " << StringPrintf("0x%02x", br->getBits(8));
+            MY_LOGV("      tag = 0x%02x", br->getBits(8));
 
             unsigned descLength = br->getBits(8);
-            LOG(VERBOSE) << "      len = " << descLength;
+            LOGV("      len = %u", descLength);
 
             CHECK_GE(info_bytes_remaining, 2 + descLength);
 
@@ -209,7 +212,7 @@
 
     CHECK_EQ(infoBytesRemaining, 0u);
 
-    LOG(VERBOSE) << "  CRC = " << StringPrintf("0x%08x", br->getBits(32));
+    MY_LOGV("  CRC = 0x%08x", br->getBits(32));
 }
 
 sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
@@ -255,7 +258,7 @@
     }
 
     size_t payloadSizeBits = br->numBitsLeft();
-    CHECK_EQ(payloadSizeBits % 8, 0u);
+    CHECK((payloadSizeBits % 8) == 0);
 
     CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
 
@@ -266,16 +269,15 @@
 void ATSParser::Stream::parsePES(ABitReader *br) {
     unsigned packet_startcode_prefix = br->getBits(24);
 
-    LOG(VERBOSE) << "packet_startcode_prefix = "
-              << StringPrintf("0x%08x", packet_startcode_prefix);
+    LOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
 
     CHECK_EQ(packet_startcode_prefix, 0x000001u);
 
     unsigned stream_id = br->getBits(8);
-    LOG(VERBOSE) << "stream_id = " << StringPrintf("0x%02x", stream_id);
+    LOGV("stream_id = 0x%02x", stream_id);
 
     unsigned PES_packet_length = br->getBits(16);
-    LOG(VERBOSE) << "PES_packet_length = " << PES_packet_length;
+    LOGV("PES_packet_length = %u", PES_packet_length);
 
     if (stream_id != 0xbc  // program_stream_map
             && stream_id != 0xbe  // padding_stream
@@ -287,33 +289,32 @@
             && stream_id != 0xf8) {  // H.222.1 type E
         CHECK_EQ(br->getBits(2), 2u);
 
-        LOG(VERBOSE) << "PES_scrambling_control = " << br->getBits(2);
-        LOG(VERBOSE) << "PES_priority = " << br->getBits(1);
-        LOG(VERBOSE) << "data_alignment_indicator = " << br->getBits(1);
-        LOG(VERBOSE) << "copyright = " << br->getBits(1);
-        LOG(VERBOSE) << "original_or_copy = " << br->getBits(1);
+        MY_LOGV("PES_scrambling_control = %u", br->getBits(2));
+        MY_LOGV("PES_priority = %u", br->getBits(1));
+        MY_LOGV("data_alignment_indicator = %u", br->getBits(1));
+        MY_LOGV("copyright = %u", br->getBits(1));
+        MY_LOGV("original_or_copy = %u", br->getBits(1));
 
         unsigned PTS_DTS_flags = br->getBits(2);
-        LOG(VERBOSE) << "PTS_DTS_flags = " << PTS_DTS_flags;
+        LOGV("PTS_DTS_flags = %u", PTS_DTS_flags);
 
         unsigned ESCR_flag = br->getBits(1);
-        LOG(VERBOSE) << "ESCR_flag = " << ESCR_flag;
+        LOGV("ESCR_flag = %u", ESCR_flag);
 
         unsigned ES_rate_flag = br->getBits(1);
-        LOG(VERBOSE) << "ES_rate_flag = " << ES_rate_flag;
+        LOGV("ES_rate_flag = %u", ES_rate_flag);
 
         unsigned DSM_trick_mode_flag = br->getBits(1);
-        LOG(VERBOSE) << "DSM_trick_mode_flag = " << DSM_trick_mode_flag;
+        LOGV("DSM_trick_mode_flag = %u", DSM_trick_mode_flag);
 
         unsigned additional_copy_info_flag = br->getBits(1);
-        LOG(VERBOSE) << "additional_copy_info_flag = "
-                  << additional_copy_info_flag;
+        LOGV("additional_copy_info_flag = %u", additional_copy_info_flag);
 
-        LOG(VERBOSE) << "PES_CRC_flag = " << br->getBits(1);
-        LOG(VERBOSE) << "PES_extension_flag = " << br->getBits(1);
+        MY_LOGV("PES_CRC_flag = %u", br->getBits(1));
+        MY_LOGV("PES_extension_flag = %u", br->getBits(1));
 
         unsigned PES_header_data_length = br->getBits(8);
-        LOG(VERBOSE) << "PES_header_data_length = " << PES_header_data_length;
+        LOGV("PES_header_data_length = %u", PES_header_data_length);
 
         unsigned optional_bytes_remaining = PES_header_data_length;
 
@@ -331,8 +332,8 @@
             PTS |= br->getBits(15);
             CHECK_EQ(br->getBits(1), 1u);
 
-            LOG(VERBOSE) << "PTS = " << PTS;
-            // LOG(INFO) << "PTS = " << PTS / 90000.0f << " secs";
+            LOGV("PTS = %llu", PTS);
+            // LOGI("PTS = %.2f secs", PTS / 90000.0f);
 
             optional_bytes_remaining -= 5;
 
@@ -348,7 +349,7 @@
                 DTS |= br->getBits(15);
                 CHECK_EQ(br->getBits(1), 1u);
 
-                LOG(VERBOSE) << "DTS = " << DTS;
+                LOGV("DTS = %llu", DTS);
 
                 optional_bytes_remaining -= 5;
             }
@@ -366,8 +367,8 @@
             ESCR |= br->getBits(15);
             CHECK_EQ(br->getBits(1), 1u);
 
-            LOG(VERBOSE) << "ESCR = " << ESCR;
-            LOG(VERBOSE) << "ESCR_extension = " << br->getBits(9);
+            LOGV("ESCR = %llu", ESCR);
+            MY_LOGV("ESCR_extension = %u", br->getBits(9));
 
             CHECK_EQ(br->getBits(1), 1u);
 
@@ -378,7 +379,7 @@
             CHECK_GE(optional_bytes_remaining, 3u);
 
             CHECK_EQ(br->getBits(1), 1u);
-            LOG(VERBOSE) << "ES_rate = " << br->getBits(22);
+            MY_LOGV("ES_rate = %u", br->getBits(22));
             CHECK_EQ(br->getBits(1), 1u);
 
             optional_bytes_remaining -= 3;
@@ -388,25 +389,27 @@
 
         // 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);
 
-            LOG(VERBOSE) << "There's " << (payloadSizeBits / 8)
-                         << " bytes of payload.";
+            LOGV("There's %d bytes of payload.", payloadSizeBits / 8);
         }
     } else if (stream_id == 0xbe) {  // padding_stream
         CHECK_NE(PES_packet_length, 0u);
@@ -422,9 +425,7 @@
         return;
     }
 
-    LOG(VERBOSE) << "flushing stream "
-                 << StringPrintf("0x%04x", mElementaryPID)
-                 << " size = " << mBuffer->size();
+    LOGV("flushing stream 0x%04x size = %d", mElementaryPID, mBuffer->size());
 
     ABitReader br(mBuffer->data(), mBuffer->size());
     parsePES(&br);
@@ -493,7 +494,7 @@
     CHECK(picParamSet != NULL);
 
     buffer->setRange(stopOffset, size - stopOffset);
-    LOG(INFO) << "buffer has " << buffer->size() << " bytes left.";
+    LOGV("buffer has %d bytes left.", buffer->size());
 
     size_t csdSize =
         1 + 3 + 1 + 1
@@ -529,6 +530,8 @@
     const uint8_t *data = *_data;
     size_t size = *_size;
 
+    // hexdump(data, size);
+
     *nalStart = NULL;
     *nalSize = 0;
 
@@ -574,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;
 
@@ -593,6 +601,7 @@
     const uint8_t *nalStart;
     size_t nalSize;
     while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) {
+        // hexdump(nalStart, nalSize);
         totalSize += 4 + nalSize;
     }
 
@@ -617,15 +626,15 @@
     CHECK_EQ(br.getBits(2), 0u);
     br.getBits(1);  // protection_absent
     unsigned profile = br.getBits(2);
-    LOG(INFO) << "profile = " << 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);
 
-    LOG(INFO) << "sampling_freq_index = " << sampling_freq_index;
-    LOG(INFO) << "channel_configuration = " << 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[] = {
@@ -671,8 +680,7 @@
 void ATSParser::Stream::onPayloadData(
         unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
         const uint8_t *data, size_t size) {
-    LOG(VERBOSE) << "onPayloadData mStreamType="
-                 << StringPrintf("0x%02x", mStreamType);
+    LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
 
     sp<ABuffer> buffer;
 
@@ -710,8 +718,8 @@
             sp<ABuffer> csd =
                 FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount);
 
-            LOG(INFO) << "sampleRate = " << sampleRate;
-            LOG(INFO) << "channelCount = " << channelCount;
+            LOGV("sampleRate = %d", sampleRate);
+            LOGV("channelCount = %d", channelCount);
 
             meta->setInt32(kKeySampleRate, sampleRate);
             meta->setInt32(kKeyChannelCount, channelCount);
@@ -719,7 +727,7 @@
             meta->setData(kKeyESDS, 0, csd->data(), csd->size());
         }
 
-        LOG(INFO) << "created source!";
+        LOGV("created source!");
         mSource = new AnotherPacketSource(meta);
 
         // fall through
@@ -777,12 +785,7 @@
 
             CHECK_LE(scan, buffer->size());
 
-            LOG(VERBOSE)
-                << "found aac raw data block at ["
-                << StringPrintf("0x%08x", offset)
-                << " ; "
-                << StringPrintf("0x%08x", scan)
-                << ")";
+            LOGV("found aac raw data block at [0x%08x ; 0x%08x)", offset, scan);
 
             memmove(&buffer->data()[dstOffset], &buffer->data()[offset],
                     scan - offset);
@@ -825,50 +828,48 @@
 
 void ATSParser::parseProgramAssociationTable(ABitReader *br) {
     unsigned table_id = br->getBits(8);
-    LOG(VERBOSE) << "  table_id = " << table_id;
+    LOGV("  table_id = %u", table_id);
     CHECK_EQ(table_id, 0x00u);
 
     unsigned section_syntax_indictor = br->getBits(1);
-    LOG(VERBOSE) << "  section_syntax_indictor = " << section_syntax_indictor;
+    LOGV("  section_syntax_indictor = %u", section_syntax_indictor);
     CHECK_EQ(section_syntax_indictor, 1u);
 
     CHECK_EQ(br->getBits(1), 0u);
-    LOG(VERBOSE) << "  reserved = " << br->getBits(2);
+    MY_LOGV("  reserved = %u", br->getBits(2));
 
     unsigned section_length = br->getBits(12);
-    LOG(VERBOSE) << "  section_length = " << section_length;
+    LOGV("  section_length = %u", section_length);
     CHECK((section_length & 0xc00) == 0);
 
-    LOG(VERBOSE) << "  transport_stream_id = " << br->getBits(16);
-    LOG(VERBOSE) << "  reserved = " << br->getBits(2);
-    LOG(VERBOSE) << "  version_number = " << br->getBits(5);
-    LOG(VERBOSE) << "  current_next_indicator = " << br->getBits(1);
-    LOG(VERBOSE) << "  section_number = " << br->getBits(8);
-    LOG(VERBOSE) << "  last_section_number = " << br->getBits(8);
+    MY_LOGV("  transport_stream_id = %u", br->getBits(16));
+    MY_LOGV("  reserved = %u", br->getBits(2));
+    MY_LOGV("  version_number = %u", br->getBits(5));
+    MY_LOGV("  current_next_indicator = %u", br->getBits(1));
+    MY_LOGV("  section_number = %u", br->getBits(8));
+    MY_LOGV("  last_section_number = %u", br->getBits(8));
 
     size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);
     CHECK_EQ((numProgramBytes % 4), 0u);
 
     for (size_t i = 0; i < numProgramBytes / 4; ++i) {
         unsigned program_number = br->getBits(16);
-        LOG(VERBOSE) << "    program_number = " << program_number;
+        LOGV("    program_number = %u", program_number);
 
-        LOG(VERBOSE) << "    reserved = " << br->getBits(3);
+        MY_LOGV("    reserved = %u", br->getBits(3));
 
         if (program_number == 0) {
-            LOG(VERBOSE) << "    network_PID = "
-                      << StringPrintf("0x%04x", br->getBits(13));
+            MY_LOGV("    network_PID = 0x%04x", br->getBits(13));
         } else {
             unsigned programMapPID = br->getBits(13);
 
-            LOG(VERBOSE) << "    program_map_PID = "
-                      << StringPrintf("0x%04x", programMapPID);
+            LOGV("    program_map_PID = 0x%04x", programMapPID);
 
             mPrograms.push(new Program(programMapPID));
         }
     }
 
-    LOG(VERBOSE) << "  CRC = " << StringPrintf("0x%08x", br->getBits(32));
+    MY_LOGV("  CRC = 0x%08x", br->getBits(32));
 }
 
 void ATSParser::parsePID(
@@ -893,8 +894,7 @@
     }
 
     if (!handled) {
-        LOG(WARNING) << "PID " << StringPrintf("0x%04x", PID)
-                     << " not handled.";
+        LOGV("PID 0x%04x not handled.", PID);
     }
 }
 
@@ -906,28 +906,30 @@
 }
 
 void ATSParser::parseTS(ABitReader *br) {
-    LOG(VERBOSE) << "---";
+    LOGV("---");
 
     unsigned sync_byte = br->getBits(8);
     CHECK_EQ(sync_byte, 0x47u);
 
-    LOG(VERBOSE) << "transport_error_indicator = " << br->getBits(1);
+    MY_LOGV("transport_error_indicator = %u", br->getBits(1));
 
     unsigned payload_unit_start_indicator = br->getBits(1);
-    LOG(VERBOSE) << "payload_unit_start_indicator = "
-                 << payload_unit_start_indicator;
+    LOGV("payload_unit_start_indicator = %u", payload_unit_start_indicator);
 
-    LOG(VERBOSE) << "transport_priority = " << br->getBits(1);
+    MY_LOGV("transport_priority = %u", br->getBits(1));
 
     unsigned PID = br->getBits(13);
-    LOG(VERBOSE) << "PID = " << StringPrintf("0x%04x", PID);
+    LOGV("PID = 0x%04x", PID);
 
-    LOG(VERBOSE) << "transport_scrambling_control = " << br->getBits(2);
+    MY_LOGV("transport_scrambling_control = %u", br->getBits(2));
 
     unsigned adaptation_field_control = br->getBits(2);
-    LOG(VERBOSE) << "adaptation_field_control = " << adaptation_field_control;
+    LOGV("adaptation_field_control = %u", adaptation_field_control);
 
-    LOG(VERBOSE) << "continuity_counter = " << 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/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index fcf506d..4f28855 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -286,7 +286,7 @@
         return OK;
     }
 
-    sp<MemoryDealer> dealer = new MemoryDealer(8 * 1024 * 1024, "OMXHarness");
+    sp<MemoryDealer> dealer = new MemoryDealer(16 * 1024 * 1024, "OMXHarness");
     IOMX::node_id node;
 
     status_t err =
diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp
index 154ba31..b1f6e9a 100644
--- a/media/libstagefright/rtsp/AAMRAssembler.cpp
+++ b/media/libstagefright/rtsp/AAMRAssembler.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AAMRAssembler"
+#include <utils/Log.h>
+
 #include "AAMRAssembler.h"
 
 #include "ARTPSource.h"
@@ -119,9 +123,7 @@
         mNextExpectedSeqNoValid = true;
         mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
     } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
-#if VERBOSE
-        LOG(VERBOSE) << "Not the sequence number I expected";
-#endif
+        LOGV("Not the sequence number I expected");
 
         return WRONG_SEQUENCE_NUMBER;
     }
@@ -132,7 +134,7 @@
         queue->erase(queue->begin());
         ++mNextExpectedSeqNo;
 
-        LOG(VERBOSE) << "AMR packet too short.";
+        LOGV("AMR packet too short.");
 
         return MALFORMED_PACKET;
     }
@@ -150,7 +152,7 @@
             queue->erase(queue->begin());
             ++mNextExpectedSeqNo;
 
-            LOG(VERBOSE) << "Unable to parse TOC.";
+            LOGV("Unable to parse TOC.");
 
             return MALFORMED_PACKET;
         }
@@ -164,7 +166,7 @@
             queue->erase(queue->begin());
             ++mNextExpectedSeqNo;
 
-            LOG(VERBOSE) << "Illegal TOC entry.";
+            LOGV("Illegal TOC entry.");
 
             return MALFORMED_PACKET;
         }
@@ -191,7 +193,7 @@
             queue->erase(queue->begin());
             ++mNextExpectedSeqNo;
 
-            LOG(VERBOSE) << "AMR packet too short.";
+            LOGV("AMR packet too short.");
 
             return MALFORMED_PACKET;
         }
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index 6b1e292..11c6ae7 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AAVCAssembler"
+#include <utils/Log.h>
+
 #include "AAVCAssembler.h"
 
 #include "ARTPSource.h"
@@ -25,8 +29,6 @@
 
 #include <stdint.h>
 
-#define BE_VERBOSE      0
-
 namespace android {
 
 // static
@@ -70,9 +72,7 @@
         mNextExpectedSeqNoValid = true;
         mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
     } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
-#if BE_VERBOSE
-        LOG(VERBOSE) << "Not the sequence number I expected";
-#endif
+        LOGV("Not the sequence number I expected");
 
         return WRONG_SEQUENCE_NUMBER;
     }
@@ -83,7 +83,7 @@
     if (size < 1 || (data[0] & 0x80)) {
         // Corrupt.
 
-        LOG(ERROR) << "Ignoring corrupt buffer.";
+        LOGV("Ignoring corrupt buffer.");
         queue->erase(queue->begin());
 
         ++mNextExpectedSeqNo;
@@ -107,7 +107,7 @@
 
         return success ? OK : MALFORMED_PACKET;
     } else {
-        LOG(ERROR) << "Ignoring unsupported buffer (nalType=" << nalType << ")";
+        LOGV("Ignoring unsupported buffer (nalType=%d)", nalType);
 
         queue->erase(queue->begin());
         ++mNextExpectedSeqNo;
@@ -117,8 +117,8 @@
 }
 
 void AAVCAssembler::addSingleNALUnit(const sp<ABuffer> &buffer) {
-#if BE_VERBOSE
-    LOG(VERBOSE) << "addSingleNALUnit of size " << buffer->size();
+    LOGV("addSingleNALUnit of size %d", buffer->size());
+#if !LOG_NDEBUG
     hexdump(buffer->data(), buffer->size());
 #endif
 
@@ -138,7 +138,7 @@
     size_t size = buffer->size();
 
     if (size < 3) {
-        LOG(ERROR) << "Discarding too small STAP-A packet.";
+        LOGV("Discarding too small STAP-A packet.");
         return false;
     }
 
@@ -148,7 +148,7 @@
         size_t nalSize = (data[0] << 8) | data[1];
 
         if (size < nalSize + 2) {
-            LOG(ERROR) << "Discarding malformed STAP-A packet.";
+            LOGV("Discarding malformed STAP-A packet.");
             return false;
         }
 
@@ -164,7 +164,7 @@
     }
 
     if (size != 0) {
-        LOG(WARNING) << "Unexpected padding at end of STAP-A packet.";
+        LOGV("Unexpected padding at end of STAP-A packet.");
     }
 
     return true;
@@ -184,7 +184,7 @@
     CHECK((indicator & 0x1f) == 28);
 
     if (size < 2) {
-        LOG(ERROR) << "Ignoring malformed FU buffer (size = " << size << ")";
+        LOGV("Ignoring malformed FU buffer (size = %d)", size);
 
         queue->erase(queue->begin());
         ++mNextExpectedSeqNo;
@@ -194,9 +194,7 @@
     if (!(data[1] & 0x80)) {
         // Start bit not set on the first buffer.
 
-#if BE_VERBOSE
-        LOG(ERROR) << "Start bit not set on first buffer";
-#endif
+        LOGV("Start bit not set on first buffer");
 
         queue->erase(queue->begin());
         ++mNextExpectedSeqNo;
@@ -214,17 +212,13 @@
     if (data[1] & 0x40) {
         // Huh? End bit also set on the first buffer.
 
-#if BE_VERBOSE
-        LOG(WARNING) << "Grrr. This isn't fragmented at all.";
-#endif
+        LOGV("Grrr. This isn't fragmented at all.");
 
         complete = true;
     } else {
         List<sp<ABuffer> >::iterator it = ++queue->begin();
         while (it != queue->end()) {
-#if BE_VERBOSE
-            LOG(VERBOSE) << "sequence length " << totalCount;
-#endif
+            LOGV("sequence length %d", totalCount);
 
             const sp<ABuffer> &buffer = *it;
 
@@ -232,11 +226,8 @@
             size_t size = buffer->size();
 
             if ((uint32_t)buffer->int32Data() != expectedSeqNo) {
-#if BE_VERBOSE
-                LOG(VERBOSE) << "sequence not complete, expected seqNo "
-                     << expectedSeqNo << ", got "
-                     << (uint32_t)buffer->int32Data();
-#endif
+                LOGV("sequence not complete, expected seqNo %d, got %d",
+                     expectedSeqNo, (uint32_t)buffer->int32Data());
 
                 return WRONG_SEQUENCE_NUMBER;
             }
@@ -245,7 +236,7 @@
                     || data[0] != indicator
                     || (data[1] & 0x1f) != nalType
                     || (data[1] & 0x80)) {
-                LOG(ERROR) << "Ignoring malformed FU buffer.\n";
+                LOGV("Ignoring malformed FU buffer.");
 
                 // Delete the whole start of the FU.
 
@@ -296,8 +287,8 @@
     for (size_t i = 0; i < totalCount; ++i) {
         const sp<ABuffer> &buffer = *it;
 
-#if BE_VERBOSE
-        LOG(VERBOSE) << "piece #" << (i + 1) << "/" << totalCount;
+        LOGV("piece #%d/%d", i + 1, totalCount);
+#if !LOG_NDEBUG
         hexdump(buffer->data(), buffer->size());
 #endif
 
@@ -311,9 +302,7 @@
 
     addSingleNALUnit(unit);
 
-#if BE_VERBOSE
-    LOG(VERBOSE) << "successfully assembled a NAL unit from fragments.";
-#endif
+    LOGV("successfully assembled a NAL unit from fragments.");
 
     return OK;
 }
@@ -321,9 +310,7 @@
 void AAVCAssembler::submitAccessUnit() {
     CHECK(!mNALUnits.empty());
 
-#if BE_VERBOSE
-    LOG(VERBOSE) << "Access unit complete (" << mNALUnits.size() << " nal units)";
-#endif
+    LOGV("Access unit complete (%d nal units)", mNALUnits.size());
 
     size_t totalSize = 0;
     for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
@@ -373,7 +360,7 @@
 
 void AAVCAssembler::packetLost() {
     CHECK(mNextExpectedSeqNoValid);
-    LOG(VERBOSE) << "packetLost (expected " << mNextExpectedSeqNo << ")";
+    LOGV("packetLost (expected %d)", mNextExpectedSeqNo);
 
     ++mNextExpectedSeqNo;
 
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index f68a35b..13988cd 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AMPEG4ElementaryAssembler"
+#include <utils/Log.h>
+
 #include "AMPEG4ElementaryAssembler.h"
 
 #include "ARTPSource.h"
@@ -28,8 +32,6 @@
 #include <ctype.h>
 #include <stdint.h>
 
-#define BE_VERBOSE      0
-
 namespace android {
 
 static bool GetAttribute(const char *s, const char *key, AString *value) {
@@ -201,9 +203,7 @@
         mNextExpectedSeqNoValid = true;
         mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
     } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
-#if BE_VERBOSE
-        LOG(VERBOSE) << "Not the sequence number I expected";
-#endif
+        LOGV("Not the sequence number I expected");
 
         return WRONG_SEQUENCE_NUMBER;
     }
@@ -336,9 +336,7 @@
 void AMPEG4ElementaryAssembler::submitAccessUnit() {
     CHECK(!mPackets.empty());
 
-#if BE_VERBOSE
-    LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
-#endif
+    LOGV("Access unit complete (%d nal units)", mPackets.size());
 
     size_t totalSize = 0;
     for (List<sp<ABuffer> >::iterator it = mPackets.begin();
@@ -385,7 +383,7 @@
 
 void AMPEG4ElementaryAssembler::packetLost() {
     CHECK(mNextExpectedSeqNoValid);
-    LOG(VERBOSE) << "packetLost (expected " << mNextExpectedSeqNo << ")";
+    LOGV("packetLost (expected %d)", mNextExpectedSeqNo);
 
     ++mNextExpectedSeqNo;
 
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index b3e86eb..78754e6 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "APacketSource"
+#include <utils/Log.h>
+
 #include "APacketSource.h"
 
 #include "ASessionDescription.h"
@@ -188,7 +192,7 @@
 
         if (i == 0) {
             FindAVCDimensions(nal, width, height);
-            LOG(INFO) << "dimensions " << *width << "x" << *height;
+            LOGI("dimensions %dx%d", *width, *height);
         }
     }
 
@@ -412,7 +416,7 @@
     *width = video_object_layer_width;
     *height = video_object_layer_height;
 
-    LOG(INFO) << "VOL dimensions = " << *width << "x" << *height;
+    LOGI("VOL dimensions = %dx%d", *width, *height);
 
     return true;
 }
@@ -711,7 +715,7 @@
 void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
     int32_t damaged;
     if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
-        LOG(VERBOSE) << "discarding damaged AU";
+        LOGV("discarding damaged AU");
         return;
     }
 
@@ -765,8 +769,9 @@
     CHECK(last->meta()->findInt64("timeUs", &lastTimeUs));
 
     if (lastTimeUs < firstTimeUs) {
-        LOG(ERROR) << "Huh? Time moving backwards? "
-                   << firstTimeUs << " > " << lastTimeUs;
+        LOGE("Huh? Time moving backwards? %lld > %lld",
+             firstTimeUs, lastTimeUs);
+
         return 0;
     }
 
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 10c9e02..ded3b24 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ARTPConnection"
+#include <utils/Log.h>
+
 #include "ARTPConnection.h"
 
 #include "ARTPSource.h"
@@ -319,7 +323,7 @@
             }
 
             if (buffer->size() > 0) {
-                LOG(VERBOSE) << "Sending RR...";
+                LOGV("Sending RR...");
 
                 ssize_t n = sendto(
                         s->mRTCPSocket, buffer->data(), buffer->size(), 0,
@@ -356,7 +360,7 @@
 
     buffer->setRange(0, nbytes);
 
-    // LOG(INFO) << "received " << buffer->size() << " bytes.";
+    // LOGI("received %d bytes.", buffer->size());
 
     status_t err;
     if (receiveRTP) {
@@ -521,9 +525,8 @@
 
             default:
             {
-                LOG(WARNING) << "Unknown RTCP packet type "
-                             << (unsigned)data[1]
-                             << " of size " << headerLength;
+                LOGW("Unknown RTCP packet type %u of size %d",
+                     (unsigned)data[1], headerLength);
                 break;
             }
         }
@@ -567,10 +570,10 @@
     uint32_t rtpTime = u32at(&data[16]);
 
 #if 0
-    LOG(INFO) << StringPrintf(
-            "XXX timeUpdate: ssrc=0x%08x, rtpTime %u == ntpTime %.3f",
-            id,
-            rtpTime, (ntpTime >> 32) + (double)(ntpTime & 0xffffffff) / (1ll << 32));
+    LOGI("XXX timeUpdate: ssrc=0x%08x, rtpTime %u == ntpTime %.3f",
+         id,
+         rtpTime,
+         (ntpTime >> 32) + (double)(ntpTime & 0xffffffff) / (1ll << 32));
 #endif
 
     sp<ARTPSource> source = findSource(s, id);
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 8ce93b7..39c6619 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ARTPSession"
+#include <utils/Log.h>
+
 #include "ARTPSession.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
@@ -51,24 +55,24 @@
         if (!mDesc->findAttribute(i, "c=", &connection)) {
             // No per-stream connection information, try global fallback.
             if (!mDesc->findAttribute(0, "c=", &connection)) {
-                LOG(ERROR) << "Unable to find connection attribtue.";
+                LOGE("Unable to find connection attribute.");
                 return mInitCheck;
             }
         }
         if (!(connection == "IN IP4 127.0.0.1")) {
-            LOG(ERROR) << "We only support localhost connections for now.";
+            LOGE("We only support localhost connections for now.");
             return mInitCheck;
         }
 
         unsigned port;
         if (!validateMediaFormat(i, &port) || (port & 1) != 0) {
-            LOG(ERROR) << "Invalid media format.";
+            LOGE("Invalid media format.");
             return mInitCheck;
         }
 
         sp<APacketSource> source = new APacketSource(mDesc, i);
         if (source->initCheck() != OK) {
-            LOG(ERROR) << "Unsupported format.";
+            LOGE("Unsupported format.");
             return mInitCheck;
         }
 
@@ -157,9 +161,8 @@
             printf("access unit complete size=%d\tntp-time=0x%016llx\n",
                    accessUnit->size(), ntpTime);
 #else
-            LOG(INFO) << "access unit complete, "
-                      << "size=" << accessUnit->size() << ", "
-                      << "ntp-time=" << ntpTime;
+            LOGI("access unit complete, size=%d, ntp-time=%llu",
+                 accessUnit->size(), ntpTime);
             hexdump(accessUnit->data(), accessUnit->size());
 #endif
 #endif
@@ -169,9 +172,8 @@
             CHECK(!memcmp("\x00\x00\x00\x01", accessUnit->data(), 4));
             unsigned x = accessUnit->data()[4];
 
-            LOG(INFO) << "access unit complete: "
-                      << StringPrintf("nalType=0x%02x, nalRefIdc=0x%02x",
-                                      x & 0x1f, (x & 0x60) >> 5);
+            LOGI("access unit complete: nalType=0x%02x, nalRefIdc=0x%02x",
+                 x & 0x1f, (x & 0x60) >> 5);
 #endif
 
             accessUnit->meta()->setInt64("ntp-time", ntpTime);
@@ -181,7 +183,7 @@
             int32_t damaged;
             if (accessUnit->meta()->findInt32("damaged", &damaged)
                     && damaged != 0) {
-                LOG(INFO) << "ignoring damaged AU";
+                LOGI("ignoring damaged AU");
             } else
 #endif
             {
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 9656ba2..2518264 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ARTPSource"
+#include <utils/Log.h>
+
 #include "ARTPSource.h"
 
 #include "AAMRAssembler.h"
@@ -27,8 +31,6 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 
-#define BE_VERBOSE      0
-
 namespace android {
 
 static const uint32_t kSourceID = 0xdeadbeef;
@@ -83,14 +85,10 @@
             && mAssembler != NULL) {
         mAssembler->onPacketReceived(this);
     }
-
-    dump();
 }
 
 void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) {
-#if BE_VERBOSE
-    LOG(VERBOSE) << "timeUpdate";
-#endif
+    LOGV("timeUpdate");
 
     mLastNTPTime = ntpTime;
     mLastNTPTimeUpdateUs = ALooper::GetNowUs();
@@ -173,7 +171,7 @@
     }
 
     if (it != mQueue.end() && (uint32_t)(*it)->int32Data() == seqNum) {
-        LOG(WARNING) << "Discarding duplicate buffer";
+        LOGW("Discarding duplicate buffer");
         return false;
     }
 
@@ -182,89 +180,6 @@
     return true;
 }
 
-void ARTPSource::dump() const {
-    if ((mNumBuffersReceived % 128) != 0) {
-        return;
-    }
-
-#if 0
-    if (mAssembler == NULL) {
-        char tmp[20];
-        sprintf(tmp, "0x%08x", mID);
-
-        int32_t numMissing = 0;
-
-        if (!mQueue.empty()) {
-            List<sp<ABuffer> >::const_iterator it = mQueue.begin();
-            uint32_t expectedSeqNum = (uint32_t)(*it)->int32Data();
-            ++expectedSeqNum;
-            ++it;
-
-            for (; it != mQueue.end(); ++it) {
-                uint32_t seqNum = (uint32_t)(*it)->int32Data();
-                CHECK_GE(seqNum, expectedSeqNum);
-
-                if (seqNum != expectedSeqNum) {
-                    numMissing += seqNum - expectedSeqNum;
-                    expectedSeqNum = seqNum;
-                }
-
-                ++expectedSeqNum;
-            }
-        }
-
-        LOG(VERBOSE) << "[" << tmp << "] Missing " << numMissing
-             << " / " << (mNumBuffersReceived + numMissing) << " packets. ("
-             << (100.0 * numMissing / (mNumBuffersReceived + numMissing))
-             << " %%)";
-    }
-#endif
-
-#if 0
-    AString out;
-
-    out.append(tmp);
-    out.append(" [");
-
-    List<sp<ABuffer> >::const_iterator it = mQueue.begin();
-    while (it != mQueue.end()) {
-        uint32_t start = (uint32_t)(*it)->int32Data();
-
-        out.append(start);
-
-        ++it;
-        uint32_t expected = start + 1;
-
-        while (it != mQueue.end()) {
-            uint32_t seqNum = (uint32_t)(*it)->int32Data();
-
-            if (seqNum != expected) {
-                if (expected > start + 1) {
-                    out.append("-");
-                    out.append(expected - 1);
-                }
-                out.append(", ");
-                break;
-            }
-
-            ++it;
-            ++expected;
-        }
-
-        if (it == mQueue.end()) {
-            if (expected > start + 1) {
-                out.append("-");
-                out.append(expected - 1);
-            }
-        }
-    }
-
-    out.append("]");
-
-    LOG(VERBOSE) << out;
-#endif
-}
-
 uint64_t ARTPSource::RTP2NTP(uint32_t rtpTime) const {
     CHECK_EQ(mNumTimes, 2u);
 
@@ -291,7 +206,7 @@
     mLastFIRRequestUs = nowUs;
 
     if (buffer->size() + 20 > buffer->capacity()) {
-        LOG(WARNING) << "RTCP buffer too small to accomodate FIR.";
+        LOGW("RTCP buffer too small to accomodate FIR.");
         return;
     }
 
@@ -324,12 +239,12 @@
 
     buffer->setRange(buffer->offset(), buffer->size() + 20);
 
-    LOG(VERBOSE) << "Added FIR request.";
+    LOGV("Added FIR request.");
 }
 
 void ARTPSource::addReceiverReport(const sp<ABuffer> &buffer) {
     if (buffer->size() + 32 > buffer->capacity()) {
-        LOG(WARNING) << "RTCP buffer too small to accomodate RR.";
+        LOGW("RTCP buffer too small to accomodate RR.");
         return;
     }
 
diff --git a/media/libstagefright/rtsp/ARTPSource.h b/media/libstagefright/rtsp/ARTPSource.h
index 252d6d6..e62c3f1 100644
--- a/media/libstagefright/rtsp/ARTPSource.h
+++ b/media/libstagefright/rtsp/ARTPSource.h
@@ -72,7 +72,6 @@
     uint64_t RTP2NTP(uint32_t rtpTime) const;
 
     bool queuePacket(const sp<ABuffer> &buffer);
-    void dump() const;
 
     DISALLOW_EVIL_CONSTRUCTORS(ARTPSource);
 };
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index ce1ee0e..155fd96 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -1,3 +1,23 @@
+/*
+ * 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 "ARTPWriter"
+#include <utils/Log.h>
+
 #include "ARTPWriter.h"
 
 #include <fcntl.h>
@@ -249,7 +269,7 @@
     status_t err = mSource->read(&mediaBuf);
 
     if (err != OK) {
-        LOG(INFO) << "reached EOS.";
+        LOGI("reached EOS.");
 
         Mutex::Autolock autoLock(mLock);
         mFlags |= kFlagEOS;
@@ -257,7 +277,7 @@
     }
 
     if (mediaBuf->range_length() > 0) {
-        LOG(VERBOSE) << "read buffer of size " << mediaBuf->range_length();
+        LOGV("read buffer of size %d", mediaBuf->range_length());
 
         if (mMode == H264) {
             StripStartcode(mediaBuf);
@@ -500,7 +520,7 @@
         sdp.append("a=fmtp:" PT_STR " octed-align\r\n");
     }
 
-    LOG(INFO) << sdp;
+    LOGI("%s", sdp.c_str());
 }
 
 void ARTPWriter::makeH264SPropParamSets(MediaBuffer *buffer) {
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index da4a73a..5ec03b2 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ARTSPConnection"
+#include <utils/Log.h>
+
 #include "ARTSPConnection.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
@@ -41,7 +45,7 @@
 
 ARTSPConnection::~ARTSPConnection() {
     if (mSocket >= 0) {
-        LOG(ERROR) << "Connection is still open, closing the socket.";
+        LOGE("Connection is still open, closing the socket.");
         close(mSocket);
         mSocket = -1;
     }
@@ -184,7 +188,7 @@
     AString host, path;
     unsigned port;
     if (!ParseURL(url.c_str(), &host, &port, &path)) {
-        LOG(ERROR) << "Malformed rtsp url " << url;
+        LOGE("Malformed rtsp url %s", url.c_str());
 
         reply->setInt32("result", ERROR_MALFORMED);
         reply->post();
@@ -195,7 +199,7 @@
 
     struct hostent *ent = gethostbyname(host.c_str());
     if (ent == NULL) {
-        LOG(ERROR) << "Unknown host " << host;
+        LOGE("Unknown host %s", host.c_str());
 
         reply->setInt32("result", -ENOENT);
         reply->post();
@@ -300,7 +304,7 @@
     CHECK_EQ(optionLen, (socklen_t)sizeof(err));
 
     if (err != 0) {
-        LOG(ERROR) << "err = " << err << " (" << strerror(err) << ")";
+        LOGE("err = %d (%s)", err, strerror(err));
 
         reply->setInt32("result", -err);
 
@@ -343,7 +347,7 @@
 
     request.insert(cseqHeader, i + 2);
 
-    LOG(VERBOSE) << request;
+    LOGV("%s", request.c_str());
 
     size_t numBytesSent = 0;
     while (numBytesSent < request.size()) {
@@ -353,7 +357,7 @@
 
         if (n == 0) {
             // Server closed the connection.
-            LOG(ERROR) << "Server unexpectedly closed the connection.";
+            LOGE("Server unexpectedly closed the connection.");
 
             reply->setInt32("result", ERROR_IO);
             reply->post();
@@ -363,7 +367,7 @@
                 continue;
             }
 
-            LOG(ERROR) << "Error sending rtsp request.";
+            LOGE("Error sending rtsp request.");
             reply->setInt32("result", -errno);
             reply->post();
             return;
@@ -438,14 +442,14 @@
         ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
         if (n == 0) {
             // Server closed the connection.
-            LOG(ERROR) << "Server unexpectedly closed the connection.";
+            LOGE("Server unexpectedly closed the connection.");
             return ERROR_IO;
         } else if (n < 0) {
             if (errno == EINTR) {
                 continue;
             }
 
-            LOG(ERROR) << "Error reading rtsp response.";
+            LOGE("Error reading rtsp response.");
             return -errno;
         }
 
@@ -516,7 +520,7 @@
             notify->setObject("buffer", buffer);
             notify->post();
         } else {
-            LOG(WARNING) << "received binary data, but no one cares.";
+            LOGW("received binary data, but no one cares.");
         }
 
         return true;
@@ -525,7 +529,7 @@
     sp<ARTSPResponse> response = new ARTSPResponse;
     response->mStatusLine = statusLine;
 
-    LOG(INFO) << "status: " << response->mStatusLine;
+    LOGI("status: %s", response->mStatusLine.c_str());
 
     ssize_t space1 = response->mStatusLine.find(" ");
     if (space1 < 0) {
@@ -555,7 +559,7 @@
             break;
         }
 
-        LOG(VERBOSE) << "line: " << line;
+        LOGV("line: %s", line.c_str());
 
         ssize_t colonPos = line.find(":");
         if (colonPos < 0) {
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 4ea7fda..0db3595 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ASessionDescription"
+#include <utils/Log.h>
+
 #include "ASessionDescription.h"
 
 #include <media/stagefright/foundation/ADebug.h>
@@ -49,7 +53,7 @@
     mFormats.push(AString("[root]"));
 
     AString desc((const char *)data, size);
-    LOG(INFO) << desc;
+    LOGI("%s", desc.c_str());
 
     size_t i = 0;
     for (;;) {
@@ -102,7 +106,7 @@
                 key.trim();
                 value.trim();
 
-                LOG(VERBOSE) << "adding '" << key << "' => '" << value << "'";
+                LOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
 
                 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
                 break;
@@ -110,7 +114,8 @@
 
             case 'm':
             {
-                LOG(VERBOSE) << "new section '" << AString(line, 2, line.size() - 2) << "'";
+                LOGV("new section '%s'",
+                     AString(line, 2, line.size() - 2).c_str());
 
                 mTracks.push(Attribs());
                 mFormats.push(AString(line, 2, line.size() - 2));
@@ -129,7 +134,7 @@
                 key.trim();
                 value.trim();
 
-                LOG(VERBOSE) << "adding '" << key << "' => '" << value << "'";
+                LOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
 
                 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
                 break;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 526bef3..a31b2b2 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -18,6 +18,10 @@
 
 #define MY_HANDLER_H_
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MyHandler"
+#include <utils/Log.h>
+
 #include "APacketSource.h"
 #include "ARTPConnection.h"
 #include "ARTSPConnection.h"
@@ -137,8 +141,8 @@
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
 
-                LOG(INFO) << "connection request completed with result "
-                     << result << " (" << strerror(-result) << ")";
+                LOGI("connection request completed with result %d (%s)",
+                     result, strerror(-result));
 
                 if (result == OK) {
                     AString request;
@@ -173,8 +177,8 @@
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
 
-                LOG(INFO) << "DESCRIBE completed with result "
-                     << result << " (" << strerror(-result) << ")";
+                LOGI("DESCRIBE completed with result %d (%s)",
+                     result, strerror(-result));
 
                 if (result == OK) {
                     sp<RefBase> obj;
@@ -251,8 +255,8 @@
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
 
-                LOG(INFO) << "SETUP(" << index << ") completed with result "
-                     << result << " (" << strerror(-result) << ")";
+                LOGI("SETUP(%d) completed with result %d (%s)",
+                     index, result, strerror(-result));
 
                 if (result == OK) {
                     CHECK(track != NULL);
@@ -326,8 +330,8 @@
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
 
-                LOG(INFO) << "PLAY completed with result "
-                     << result << " (" << strerror(-result) << ")";
+                LOGI("PLAY completed with result %d (%s)",
+                     result, strerror(-result));
 
                 if (result == OK) {
                     sp<RefBase> obj;
@@ -404,8 +408,8 @@
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
 
-                LOG(INFO) << "TEARDOWN completed with result "
-                     << result << " (" << strerror(-result) << ")";
+                LOGI("TEARDOWN completed with result %d (%s)",
+                     result, strerror(-result));
 
                 sp<AMessage> reply = new AMessage('disc', id());
 
@@ -431,7 +435,7 @@
             case 'chek':
             {
                 if (mNumAccessUnitsReceived == 0) {
-                    LOG(INFO) << "stream ended? aborting.";
+                    LOGI("stream ended? aborting.");
                     (new AMessage('abor', id()))->post();
                     break;
                 }
@@ -461,7 +465,7 @@
                 CHECK(msg->findSize("track-index", &trackIndex));
 
                 if (trackIndex >= mTracks.size()) {
-                    LOG(ERROR) << "late packets ignored.";
+                    LOGV("late packets ignored.");
                     break;
                 }
 
@@ -469,7 +473,7 @@
 
                 int32_t eos;
                 if (msg->findInt32("eos", &eos)) {
-                    LOG(INFO) << "received BYE on track index " << trackIndex;
+                    LOGI("received BYE on track index %d", trackIndex);
 #if 0
                     track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
 #endif
@@ -484,14 +488,13 @@
                 uint32_t seqNum = (uint32_t)accessUnit->int32Data();
 
                 if (mSeekPending) {
-                    LOG(INFO) << "we're seeking, dropping stale packet.";
+                    LOGV("we're seeking, dropping stale packet.");
                     break;
                 }
 
                 if (seqNum < track->mFirstSeqNumInSegment) {
-                    LOG(INFO) << "dropping stale access-unit "
-                              << "(" << seqNum << " < "
-                              << track->mFirstSeqNumInSegment << ")";
+                    LOGV("dropping stale access-unit (%d < %d)",
+                         seqNum, track->mFirstSeqNumInSegment);
                     break;
                 }
 
@@ -506,10 +509,8 @@
                 if (track->mNewSegment) {
                     track->mNewSegment = false;
 
-                    LOG(VERBOSE) << "first segment unit ntpTime="
-                              << StringPrintf("0x%016llx", ntpTime)
-                              << " rtpTime=" << rtpTime
-                              << " seq=" << seqNum;
+                    LOGV("first segment unit ntpTime=0x%016llx rtpTime=%u seq=%d",
+                         ntpTime, rtpTime, seqNum);
                 }
 
                 if (mFirstAccessUnit) {
@@ -535,7 +536,7 @@
                 int32_t damaged;
                 if (accessUnit->meta()->findInt32("damaged", &damaged)
                         && damaged != 0) {
-                    LOG(INFO) << "ignoring damaged AU";
+                    LOGI("ignoring damaged AU");
                 } else
 #endif
                 {
@@ -608,8 +609,8 @@
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
 
-                LOG(INFO) << "PLAY completed with result "
-                     << result << " (" << strerror(-result) << ")";
+                LOGI("PLAY completed with result %d (%s)",
+                     result, strerror(-result));
 
                 if (result == OK) {
                     sp<RefBase> obj;
@@ -622,12 +623,12 @@
                     } else {
                         parsePlayResponse(response);
 
-                        LOG(INFO) << "seek completed.";
+                        LOGI("seek completed.");
                     }
                 }
 
                 if (result != OK) {
-                    LOG(ERROR) << "seek failed, aborting.";
+                    LOGE("seek failed, aborting.");
                     (new AMessage('abor', id()))->post();
                 }
 
@@ -652,11 +653,10 @@
             {
                 if (!mReceivedFirstRTCPPacket) {
                     if (mTryTCPInterleaving) {
-                        LOG(WARNING) << "Never received any data, disconnecting.";
+                        LOGW("Never received any data, disconnecting.");
                         (new AMessage('abor', id()))->post();
                     } else {
-                        LOG(WARNING)
-                            << "Never received any data, switching transports.";
+                        LOGW("Never received any data, switching transports.");
 
                         mTryTCPInterleaving = true;
 
@@ -700,7 +700,7 @@
         }
 
         AString range = response->mHeaders.valueAt(i);
-        LOG(VERBOSE) << "Range: " << range;
+        LOGV("Range: %s", range.c_str());
 
         AString val;
         CHECK(GetAttribute(range.c_str(), "npt", &val));
@@ -724,7 +724,7 @@
         for (List<AString>::iterator it = streamInfos.begin();
              it != streamInfos.end(); ++it) {
             (*it).trim();
-            LOG(VERBOSE) << "streamInfo[" << n << "] = " << *it;
+            LOGV("streamInfo[%d] = %s", n, (*it).c_str());
 
             CHECK(GetAttribute((*it).c_str(), "url", &val));
 
@@ -748,8 +748,7 @@
 
             uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
 
-            LOG(VERBOSE) << "track #" << n
-                      << ": rtpTime=" << rtpTime << " <=> npt=" << npt1;
+            LOGV("track #%d: rtpTime=%u <=> ntp=%.2f", n, rtpTime, npt1);
 
             info->mPacketSource->setNormalPlayTimeMapping(
                     rtpTime, (int64_t)(npt1 * 1E6));
@@ -806,8 +805,7 @@
             new APacketSource(mSessionDesc, index);
 
         if (source->initCheck() != OK) {
-            LOG(WARNING) << "Unsupported format. Ignoring track #"
-                         << index << ".";
+            LOGW("Unsupported format. Ignoring track #%d.", index);
 
             sp<AMessage> reply = new AMessage('setu', id());
             reply->setSize("index", index);
@@ -830,7 +828,7 @@
         info->mFirstSeqNumInSegment = 0;
         info->mNewSegment = true;
 
-        LOG(VERBOSE) << "track #" << mTracks.size() << " URL=" << trackURL;
+        LOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str());
 
         AString request = "SETUP ";
         request.append(trackURL);
diff --git a/media/libstagefright/rtsp/UDPPusher.cpp b/media/libstagefright/rtsp/UDPPusher.cpp
index 28a343f..576b3ca 100644
--- a/media/libstagefright/rtsp/UDPPusher.cpp
+++ b/media/libstagefright/rtsp/UDPPusher.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "UDPPusher"
+#include <utils/Log.h>
+
 #include "UDPPusher.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
@@ -67,7 +71,7 @@
 bool UDPPusher::onPush() {
     uint32_t length;
     if (fread(&length, 1, sizeof(length), mFile) < sizeof(length)) {
-        LOG(INFO) << "No more data to push.";
+        LOGI("No more data to push.");
         return false;
     }
 
@@ -77,7 +81,7 @@
 
     sp<ABuffer> buffer = new ABuffer(length);
     if (fread(buffer->data(), 1, length, mFile) < length) {
-        LOG(ERROR) << "File truncated?.";
+        LOGE("File truncated?.");
         return false;
     }
 
@@ -89,7 +93,7 @@
 
     uint32_t timeMs;
     if (fread(&timeMs, 1, sizeof(timeMs), mFile) < sizeof(timeMs)) {
-        LOG(INFO) << "No more data to push.";
+        LOGI("No more data to push.");
         return false;
     }
 
@@ -109,7 +113,7 @@
         case kWhatPush:
         {
             if (!onPush() && !(ntohs(mRemoteAddr.sin_port) & 1)) {
-                LOG(INFO) << "emulating BYE packet";
+                LOGI("emulating BYE packet");
 
                 sp<ABuffer> buffer = new ABuffer(8);
                 uint8_t *data = buffer->data();
diff --git a/media/libstagefright/rtsp/rtp_test.cpp b/media/libstagefright/rtsp/rtp_test.cpp
index cec6c0c..f0cb5a5 100644
--- a/media/libstagefright/rtsp/rtp_test.cpp
+++ b/media/libstagefright/rtsp/rtp_test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "rtp_test"
+#include <utils/Log.h>
+
 #include <binder/ProcessState.h>
 
 #include <media/stagefright/foundation/ADebug.h>
@@ -200,8 +204,7 @@
                 continue;
             }
 
-            LOG(ERROR) << "decoder returned error "
-                       << StringPrintf("0x%08x", err);
+            LOGE("decoder returned error 0x%08x", err);
             break;
         }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
index d339e06..4e4df3b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
@@ -16,7 +16,7 @@
 
 package com.android.mediaframeworktest.functional;
 
-import android.media.Visualizer;
+import android.media.audiofx.Visualizer;
 import android.util.Log;
 
 /**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
index fd939ae..34025f69e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
@@ -19,12 +19,12 @@
 import com.android.mediaframeworktest.MediaFrameworkTest;
 import com.android.mediaframeworktest.MediaNames;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioTrack;
-import android.media.EnvironmentalReverb;
-import android.media.Equalizer;
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.Equalizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
@@ -103,14 +103,14 @@
         boolean hasEnvReverb = false;
 
         for (int i = 0; i < desc.length; i++) {
-            if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+            if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
                 hasEQ = true;
-            } if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
+            } if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
                 hasBassBoost = true;
-            } else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
                 hasVirtualizer = true;
             }
-            else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
+            else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
                 hasEnvReverb = true;
             }
         }
@@ -132,7 +132,7 @@
         AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
         assertTrue(msg+": no effects found", (desc.length != 0));
         try {
-            AudioEffect effect = new AudioEffect(desc[0].mType,
+            AudioEffect effect = new AudioEffect(desc[0].type,
                     AudioEffect.EFFECT_TYPE_NULL,
                     0,
                     0);
@@ -146,7 +146,7 @@
                 effect.release();
             }
         } catch (IllegalArgumentException e) {
-            msg = msg.concat(": Effect not found: "+desc[0].mName);
+            msg = msg.concat(": Effect not found: "+desc[0].name);
             result = false;
         } catch (UnsupportedOperationException e) {
             msg = msg.concat(": Effect library not loaded");
@@ -164,13 +164,13 @@
         assertTrue(msg+"no effects found", (desc.length != 0));
         try {
             AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_NULL,
-                    desc[0].mUuid,
+                    desc[0].uuid,
                     0,
                     0);
             assertNotNull(msg + ": could not create AudioEffect", effect);
             effect.release();
         } catch (IllegalArgumentException e) {
-            msg = msg.concat(": Effect not found: "+desc[0].mName);
+            msg = msg.concat(": Effect not found: "+desc[0].name);
             result = false;
         } catch (UnsupportedOperationException e) {
             msg = msg.concat(": Effect library not loaded");
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
index aca729e..aa5c4d7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.BassBoost;
-import android.media.Visualizer;
+import android.media.audiofx.BassBoost;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
@@ -45,6 +45,7 @@
     private String TAG = "MediaBassBoostTest";
     private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
+    private final static int TEST_VOLUME = 4;
 
     private BassBoost mBassBoost = null;
     private int mSession = -1;
@@ -192,7 +193,7 @@
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
-                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           TEST_VOLUME,
                            0);
 
         try {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
index db0db70..ba202a7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.EnvironmentalReverb;
-import android.media.Visualizer;
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
@@ -414,13 +414,13 @@
         EnergyProbe probe = null;
         AudioEffect vc = null;
         MediaPlayer mp = null;
+        AudioEffect rvb = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
                            0);
         try {
-            probe = new EnergyProbe(0);
             // creating a volume controller on output mix ensures that ro.audio.silent mutes
             // audio after the effects and not before
             vc = new AudioEffect(
@@ -433,11 +433,24 @@
             mp = new MediaPlayer();
             mp.setDataSource(MediaNames.SINE_200_1000);
             mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
-            getReverb(mp.getAudioSessionId());
-            mReverb.setRoomLevel((short)0);
-            mReverb.setReverbLevel((short)0);
-            mReverb.setDecayTime(2000);
-            mReverb.setEnabled(true);
+
+            // create reverb with UUID instead of EnvironmentalReverb constructor otherwise an
+            // auxiliary reverb will be chosen by the effect framework as we are on session 0
+            rvb = new AudioEffect(
+                        AudioEffect.EFFECT_TYPE_NULL,
+                        UUID.fromString("c7a511a0-a3bb-11df-860e-0002a5d5c51b"),
+                        0,
+                        0);
+
+            rvb.setParameter(EnvironmentalReverb.PARAM_ROOM_LEVEL, (short)0);
+            rvb.setParameter(EnvironmentalReverb.PARAM_REVERB_LEVEL, (short)0);
+            rvb.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 2000);
+            rvb.setEnabled(true);
+
+            // create probe after reverb so that it is chained behind the reverb in the
+            // effect chain
+            probe = new EnergyProbe(0);
+
             mp.prepare();
             mp.start();
             Thread.sleep(1000);
@@ -460,13 +473,15 @@
             loge(msg, "sleep() interrupted");
         }
         finally {
-            releaseReverb();
             if (mp != null) {
                 mp.release();
             }
             if (vc != null) {
                 vc.release();
             }
+            if (rvb != null) {
+                rvb.release();
+            }
             if (probe != null) {
                 probe.release();
             }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
index 7b3945d..9146fb8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.Equalizer;
-import android.media.Visualizer;
+import android.media.audiofx.Equalizer;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
@@ -48,6 +48,7 @@
     private final static int MAX_BAND_LEVEL = 1500;
     private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
     private final static int MIN_NUMBER_OF_PRESETS = 4;
+    private final static int TEST_VOLUME = 4;
     private Equalizer mEqualizer = null;
     private int mSession = -1;
 
@@ -259,7 +260,7 @@
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
-                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           TEST_VOLUME,
                            0);
         try {
             probe = new EnergyProbe(0);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
index c14319a..242e6bb 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.PresetReverb;
-import android.media.Visualizer;
+import android.media.audiofx.PresetReverb;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
@@ -257,13 +257,13 @@
         EnergyProbe probe = null;
         AudioEffect vc = null;
         MediaPlayer mp = null;
+        AudioEffect rvb = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
                            0);
         try {
-            probe = new EnergyProbe(0);
             // creating a volume controller on output mix ensures that ro.audio.silent mutes
             // audio after the effects and not before
             vc = new AudioEffect(
@@ -279,6 +279,22 @@
             getReverb(mp.getAudioSessionId());
             mReverb.setPreset((short)PresetReverb.PRESET_PLATE);
             mReverb.setEnabled(true);
+
+            // create reverb with UUID instead of PresetReverb constructor otherwise an auxiliary
+            // reverb will be chosen by the effect framework as we are on session 0
+            rvb = new AudioEffect(
+                        AudioEffect.EFFECT_TYPE_NULL,
+                        UUID.fromString("172cdf00-a3bc-11df-a72f-0002a5d5c51b"),
+                        0,
+                        0);
+
+            rvb.setParameter(PresetReverb.PARAM_PRESET, PresetReverb.PRESET_PLATE);
+            rvb.setEnabled(true);
+
+            // create probe after reverb so that it is chained behind the reverb in the
+            // effect chain
+            probe = new EnergyProbe(0);
+
             mp.prepare();
             mp.start();
             Thread.sleep(1000);
@@ -308,6 +324,9 @@
             if (vc != null) {
                 vc.release();
             }
+            if (rvb != null) {
+                rvb.release();
+            }
             if (probe != null) {
                 probe.release();
             }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
index 517d575..7a35429 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -20,10 +20,10 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.Virtualizer;
-import android.media.Visualizer;
+import android.media.audiofx.Virtualizer;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
@@ -45,6 +45,7 @@
     private String TAG = "MediaVirtualizerTest";
     private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
+    private final static int TEST_VOLUME = 4;
 
     private Virtualizer mVirtualizer = null;
     private int mSession = -1;
@@ -193,7 +194,7 @@
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
-                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           TEST_VOLUME,
                            0);
 
         try {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
index 26fdbfe..542ca8d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
@@ -20,9 +20,9 @@
 import com.android.mediaframeworktest.MediaNames;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.media.AudioEffect;
+import android.media.audiofx.AudioEffect;
 import android.media.AudioManager;
-import android.media.Visualizer;
+import android.media.audiofx.Visualizer;
 import android.media.MediaPlayer;
 
 import android.os.Looper;
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 5d6ac26..747c829 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -136,30 +136,24 @@
      */
     
     void* dso;
-    char path[PATH_MAX];
     int index = int(display);
     driver_t* hnd = 0;
-    const char* const format = "/system/lib/egl/lib%s_%s.so";
     
     char const* tag = getTag(index, impl);
     if (tag) {
-        snprintf(path, PATH_MAX, format, "GLES", tag);
-        dso = load_driver(path, cnx, EGL | GLESv1_CM | GLESv2);
+        dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2);
         if (dso) {
             hnd = new driver_t(dso);
         } else {
             // Always load EGL first
-            snprintf(path, PATH_MAX, format, "EGL", tag);
-            dso = load_driver(path, cnx, EGL);
+            dso = load_driver("EGL", tag, cnx, EGL);
             if (dso) {
                 hnd = new driver_t(dso);
 
                 // TODO: make this more automated
-                snprintf(path, PATH_MAX, format, "GLESv1_CM", tag);
-                hnd->set( load_driver(path, cnx, GLESv1_CM), GLESv1_CM );
+                hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM );
 
-                snprintf(path, PATH_MAX, format, "GLESv2", tag);
-                hnd->set( load_driver(path, cnx, GLESv2), GLESv2 );
+                hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 );
             }
         }
     }
@@ -222,12 +216,20 @@
     }
 }
 
-void *Loader::load_driver(const char* driver_absolute_path,
+void *Loader::load_driver(const char* kind, const char *tag,
         egl_connection_t* cnx, uint32_t mask)
 {
+    char driver_absolute_path[PATH_MAX];
+    const char* const search1 = "/vendor/lib/egl/lib%s_%s.so";
+    const char* const search2 = "/system/lib/egl/lib%s_%s.so";
+
+    snprintf(driver_absolute_path, PATH_MAX, search1, kind, tag);
     if (access(driver_absolute_path, R_OK)) {
-        // this happens often, we don't want to log an error
-        return 0;
+        snprintf(driver_absolute_path, PATH_MAX, search2, kind, tag);
+        if (access(driver_absolute_path, R_OK)) {
+            // this happens often, we don't want to log an error
+            return 0;
+        }
     }
 
     void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 8659b0b..580d6e4 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -74,7 +74,7 @@
     
 private:
     Loader();
-    void *load_driver(const char* driver, egl_connection_t* cnx, uint32_t mask);
+    void *load_driver(const char* kind, const char *tag, egl_connection_t* cnx, uint32_t mask);
 
     static __attribute__((noinline))
     void init_api(void* dso, 
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index df21358d..2d1a278 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -428,13 +428,14 @@
 
 // ----------------------------------------------------------------------------
 
-static void gl_no_context() {
+static int gl_no_context() {
     tls_t* tls = getTLS();
     if (tls->logCallWithNoContext == EGL_TRUE) {
         tls->logCallWithNoContext = EGL_FALSE;
         LOGE("call to OpenGL ES API with no current context "
              "(logged once per thread)");
     }
+    return 0;
 }
 
 static void early_egl_init(void) 
@@ -447,6 +448,7 @@
             (uint32_t*)(void*)&gHooksNoContext, 
             addr, 
             sizeof(gHooksNoContext));
+
     setGlThreadSpecific(&gHooksNoContext);
 }
 
@@ -1344,16 +1346,18 @@
 EGLint eglGetError(void)
 {
     EGLint result = EGL_SUCCESS;
+    EGLint err;
     for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        EGLint err = EGL_SUCCESS;
+        err = EGL_SUCCESS;
         egl_connection_t* const cnx = &gEGLImpl[i];
         if (cnx->dso)
             err = cnx->egl.eglGetError();
         if (err!=EGL_SUCCESS && result==EGL_SUCCESS)
             result = err;
     }
+    err = getError();
     if (result == EGL_SUCCESS)
-        result = getError();
+        result = err;
     return result;
 }
 
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 924737e..18dd483 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -58,6 +58,7 @@
             "ldr   r12, [r12, %[tls]] \n"                       \
             "cmp   r12, #0            \n"                       \
             "ldrne pc,  [r12, %[api]] \n"                       \
+            "mov   r0, #0             \n"                       \
             "bx    lr                 \n"                       \
             :                                                   \
             : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index d71ff76..ee29f12 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -114,6 +114,7 @@
             "ldr   r12, [r12, %[tls]] \n"                       \
             "cmp   r12, #0            \n"                       \
             "ldrne pc,  [r12, %[api]] \n"                       \
+            "mov   r0, #0             \n"                       \
             "bx    lr                 \n"                       \
             :                                                   \
             : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ade93da..6e2bfdb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -327,10 +327,7 @@
         try {
             final String value = c.moveToNext() ? c.getString(0) : null;
             if (value == null) {
-                final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
-                String serial = SystemProperties.get("ro.serialno", "");
-                random.setSeed(
-                    (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes());
+                final SecureRandom random = new SecureRandom();
                 final String newAndroidIdValue = Long.toHexString(random.nextLong());
                 Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]");
                 final ContentValues values = new ContentValues();
@@ -342,8 +339,6 @@
                 }
             }
             return true;
-        } catch (NoSuchAlgorithmException e) {
-            return false;
         } finally {
             c.close();
         }
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
index 1e4bbf5..18c77df 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
index bdc8c27..3fe08c2 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
index d0d1345..95866b1 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g_fully.png
index 5af2b05..5524e26 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
index 4211b8c..1a69d13 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
index d5ece7a..09094d1 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
index 6687b40..0f64e0f 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png
old mode 100755
new mode 100644
index ba24082..90454b0
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png
old mode 100755
new mode 100644
index 5af2b05..5524e26
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png
old mode 100755
new mode 100644
index 9909b09..a43c233
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png
old mode 100755
new mode 100644
index 0e02b8d..03a06a0
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png
old mode 100755
new mode 100644
index f84ad32..be7eed4
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png
old mode 100755
new mode 100644
index d80a8ce..2d77956
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png
old mode 100755
new mode 100644
index 31c976a..a8411fc6
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png
old mode 100755
new mode 100644
index a487f29..b135a81
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png
old mode 100755
new mode 100644
index 816085b..9ef67d1d
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png
old mode 100755
new mode 100644
index 0132019..c9196b2
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png
old mode 100755
new mode 100644
index 3903545..f7dda73
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png
old mode 100755
new mode 100644
index ed099ff..cf17eea
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png
old mode 100755
new mode 100644
index c930e4c..9e0d73c
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png
old mode 100755
new mode 100644
index 407a06c..c506f75
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png
old mode 100755
new mode 100644
index 6141f72..6258164
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png
old mode 100755
new mode 100644
index d44a4cf..eb5150f
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png
old mode 100755
new mode 100644
index 54ebd9b..2496714
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png
old mode 100755
new mode 100644
index 2fe0bbf..4aba1b1
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png
old mode 100755
new mode 100644
index e58e019..3624a8d
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
index 00a29a4..720f578 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
index 11ee0f2..07ea499 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g_fully.png
index 31c976a..a8411fc6 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
index fc135fc..f8779a6 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e_fully.png
index c299e12..ae90cc8 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
index 3d33a62..f6fe4b2 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
index f36e1eb..393963f 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
index ef5dbf4..abe961a 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
index dba9675..95bb3cd 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g_fully.png
index 3903545..f7dda73 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
index 2e5d82e..e6e9df4 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
index 0985a09..a14e8d6 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
index 7a32c43..404afff 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
index b622556..a47a482 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
index 04ec59e..cac7802 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g_fully.png
index d44a4cf..eb5150f 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
index a47b982..41e86cc 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
index 9d90e71..6c4203d 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
index 920f290..9927dbf 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
index e306ad7..56943ea 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_on.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_on.png
deleted file mode 100644
index 37fb8f7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_on.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
index adf668d..eeb7f67 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
index 7bf6b51..aee093a 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
index 78738ac..a40017f 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
index ac88143..c3b44ee 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png
index 55a2ad8..98e874a 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
index d16b3e8..aea18ed 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png
index 2511083..77e6ee4 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png
index e0799a5..c2574e1 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png
index 2385c3a..55caecf 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png b/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png
index 67c8b7f..98e5487 100644
--- a/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png b/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png
new file mode 100644
index 0000000..4b5dbe3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 423cf5a..a5d6885 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -21,7 +21,7 @@
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
 <com.android.systemui.statusbar.StatusBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="@drawable/status_bar_background"
+    android:background="@drawable/statusbar_background"
     android:orientation="vertical"
     android:focusable="true"
     android:descendantFocusability="afterDescendants"
@@ -108,6 +108,6 @@
         android:gravity="center_vertical|left"
         android:paddingLeft="6px"
         android:paddingRight="6px"
-        android:background="@drawable/status_bar_background"
+        android:background="@drawable/statusbar_background"
         />
 </com.android.systemui.statusbar.StatusBarView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index b5b1b50..a58e311 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -31,7 +31,7 @@
         android:paddingTop="3dp"
         android:paddingBottom="5dp"
         android:paddingRight="3dp"
-        android:background="@drawable/shade_header_background"
+        android:background="@drawable/title_bar_portrait"
         >
         <com.android.systemui.statusbar.CarrierLabel
             android:layout_width="0dp"
@@ -43,7 +43,7 @@
             android:paddingBottom="1dp"
             android:paddingLeft="4dp"
             android:textAppearance="?android:attr/textAppearanceLarge"
-            android:textColor="?android:attr/textColorSecondary"
+            android:textColor="#ffdfdfdf"
             />
         <TextView android:id="@+id/clear_all_button"
             android:layout_width="wrap_content"
@@ -82,7 +82,7 @@
                 <TextView android:id="@+id/noNotificationsTitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:background="@drawable/title_bar_portrait"
+                    android:background="@drawable/shade_bgcolor"
                     android:paddingLeft="5dp"
                     android:textAppearance="@style/TextAppearance.StatusBar.Title"
                     android:text="@string/status_bar_no_notifications_title"
@@ -91,7 +91,7 @@
                 <TextView android:id="@+id/ongoingTitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:background="@drawable/title_bar_portrait"
+                    android:background="@drawable/shade_bgcolor"
                     android:paddingLeft="5dp"
                     android:textAppearance="@style/TextAppearance.StatusBar.Title"
                     android:text="@string/status_bar_ongoing_events_title"
@@ -105,7 +105,7 @@
                 <TextView android:id="@+id/latestTitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:background="@drawable/title_bar_portrait"
+                    android:background="@drawable/shade_bgcolor"
                     android:paddingLeft="5dp"
                     android:textAppearance="@style/TextAppearance.StatusBar.Title"
                     android:text="@string/status_bar_latest_events_title"
diff --git a/packages/SystemUI/res/layout/status_bar_tracking.xml b/packages/SystemUI/res/layout/status_bar_tracking.xml
index a2b40e6..3b9b775 100644
--- a/packages/SystemUI/res/layout/status_bar_tracking.xml
+++ b/packages/SystemUI/res/layout/status_bar_tracking.xml
@@ -26,11 +26,12 @@
     android:paddingRight="0px"
     >
 
-    <com.android.systemui.statusbar.TrackingPatternView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        />
+    <View
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:layout_weight="1"
+         android:background="#ff212121"
+         />
 
     <com.android.systemui.statusbar.CloseDragHandle android:id="@+id/close"
         android:layout_width="match_parent"
@@ -42,7 +43,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="bottom"
             android:scaleType="fitXY"
-            android:src="@drawable/shade_handlebar"
+            android:src="@drawable/status_bar_close_on"
             />
 
     </com.android.systemui.statusbar.CloseDragHandle>
diff --git a/voip/java/android/net/sip/SessionDescription.aidl b/packages/SystemUI/res/values/colors.xml
similarity index 68%
rename from voip/java/android/net/sip/SessionDescription.aidl
rename to packages/SystemUI/res/values/colors.xml
index a120d16..4b1fcf0f 100644
--- a/voip/java/android/net/sip/SessionDescription.aidl
+++ b/packages/SystemUI/res/values/colors.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
 /*
- * Copyright (C) 2010, The Android Open Source Project
+ * Copyright 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.
@@ -13,7 +15,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.net.sip;
-
-parcelable SessionDescription;
+-->
+<resources>
+    <drawable name="shade_bgcolor">#ff282828</drawable>
+    <drawable name="notification_header_text_color">#ff969696</drawable>
+</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 816f34a4..86bcf3a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -19,7 +19,7 @@
     <style name="TextAppearance.StatusBar.Title" parent="@android:style/TextAppearance.StatusBar">
         <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
         <item name="android:textStyle">bold</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">@drawable/notification_header_text_color</item>
     </style>
 
     <style name="TextAppearance.StatusBar.IntruderAlert"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f9347b1..4f080d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -165,8 +165,10 @@
                             break;
                         }
                         case OP_REMOVE_ICON:
-                            mList.removeIcon(index);
-                            mCallbacks.removeIcon(mList.getSlot(index), index, viewIndex);
+                            if (mList.getIcon(index) != null) {
+                                mList.removeIcon(index);
+                                mCallbacks.removeIcon(mList.getSlot(index), index, viewIndex);
+                            }
                             break;
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index d98bd7d..0ca0572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.Canvas;
 import android.util.Slog;
+import android.util.Log;
 import android.view.ViewDebug;
 import android.widget.FrameLayout;
 
@@ -124,4 +125,10 @@
     public StatusBarIcon getStatusBarIcon() {
         return mIcon;
     }
+
+    protected void debug(int depth) {
+        super.debug(depth);
+        Log.d("View", debugIndent(depth) + "slot=" + mSlot);
+        Log.d("View", debugIndent(depth) + "icon=" + mIcon);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 7ccf210..0309430 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -711,21 +711,20 @@
                 ConnectivityManager.EXTRA_NETWORK_INFO));
         int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
         Slog.d(TAG, "got CONNECTIVITY_ACTION - info=" + info + ", status = " + connectionStatus);
-        if (info.isConnected() == false) return;
+
+        int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
 
         switch (info.getType()) {
         case ConnectivityManager.TYPE_MOBILE:
-            if (info.isConnected()) {
-                updateDataNetType(info.getSubtype(), connectionStatus);
-                updateDataIcon();
-                updateSignalStrength(); // apply any change in connectionStatus
-            }
+            mInetCondition = inetCondition;
+            updateDataNetType(info.getSubtype());
+            updateDataIcon();
+            updateSignalStrength(); // apply any change in connectionStatus
             break;
         case ConnectivityManager.TYPE_WIFI:
+            mInetCondition = inetCondition;
             if (info.isConnected()) {
                 mIsWifiConnected = true;
-                mInetCondition =
-                        (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
                 int iconId;
                 if (mLastWifiSignalLevel == -1) {
                     iconId = sWifiSignalImages[mInetCondition][0];
@@ -738,7 +737,6 @@
             } else {
                 mLastWifiSignalLevel = -1;
                 mIsWifiConnected = false;
-                mInetCondition = 0;
                 int iconId = sWifiSignalImages[0][0];
 
                 mService.setIcon("wifi", iconId, 0);
@@ -777,9 +775,8 @@
         @Override
         public void onDataConnectionStateChanged(int state, int networkType) {
             mDataState = state;
-            updateDataNetType(networkType, 0);
+            updateDataNetType(networkType);
             updateDataIcon();
-            updateSignalStrength(); // apply the change in connection status
         }
 
         @Override
@@ -847,7 +844,7 @@
         int[] iconList;
 
         // Display signal strength while in "emergency calls only" mode
-        if (!hasService() && !mServiceState.isEmergencyOnly()) {
+        if (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly())) {
             //Slog.d(TAG, "updateSignalStrength: no service");
             if (Settings.System.getInt(mContext.getContentResolver(),
                     Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
@@ -940,8 +937,7 @@
         return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
     }
 
-    private final void updateDataNetType(int net, int inetCondition) {
-        mInetCondition = (inetCondition > INET_CONDITION_THRESHOLD ? 1 : 0);
+    private final void updateDataNetType(int net) {
         switch (net) {
         case TelephonyManager.NETWORK_TYPE_EDGE:
             mDataIconList = sDataNetType_e[mInetCondition];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index af736aa..ea54656 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -82,6 +82,7 @@
 
 public class StatusBarService extends Service implements CommandQueue.Callbacks {
     static final String TAG = "StatusBarService";
+    static final boolean SPEW_ICONS = false;
     static final boolean SPEW = false;
 
     public static final String ACTION_STATUSBAR_START
@@ -346,8 +347,10 @@
     }
 
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
-        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                + " icon=" + icon);
+        if (SPEW_ICONS) {
+            Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+                    + " icon=" + icon);
+        }
         StatusBarIconView view = new StatusBarIconView(this, slot);
         view.set(icon);
         mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
@@ -355,14 +358,18 @@
 
     public void updateIcon(String slot, int index, int viewIndex,
             StatusBarIcon old, StatusBarIcon icon) {
-        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                + " old=" + old + " icon=" + icon);
+        if (SPEW_ICONS) {
+            Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+                    + " old=" + old + " icon=" + icon);
+        }
         StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
         view.set(icon);
     }
 
     public void removeIcon(String slot, int index, int viewIndex) {
-        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+        if (SPEW_ICONS) {
+            Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+        }
         mStatusIcons.removeViewAt(viewIndex);
     }
 
@@ -1175,50 +1182,17 @@
                     + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
             pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
         }
-        /*
-        synchronized (mNotificationData) {
-            int N = mNotificationData.ongoingCount();
-            pw.println("  ongoingCount.size=" + N);
-            for (int i=0; i<N; i++) {
-                StatusBarNotification n = mNotificationData.getOngoing(i);
-                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
-                pw.println("           data=" + n.data);
-            }
-            N = mNotificationData.latestCount();
-            pw.println("  ongoingCount.size=" + N);
-            for (int i=0; i<N; i++) {
-                StatusBarNotification n = mNotificationData.getLatest(i);
-                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
-                pw.println("           data=" + n.data);
-            }
-        }
-        */
 
-        if (false) {
-            pw.println("see the logcat for a dump of the views we have created.");
+        if (true) {
             // must happen on ui thread
             mHandler.post(new Runnable() {
                     public void run() {
-                        mStatusBarView.getLocationOnScreen(mAbsPos);
-                        Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
-                                + ") " + mStatusBarView.getWidth() + "x"
-                                + mStatusBarView.getHeight());
-                        mStatusBarView.debug();
-
-                        mExpandedView.getLocationOnScreen(mAbsPos);
-                        Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
-                                + ") " + mExpandedView.getWidth() + "x"
-                                + mExpandedView.getHeight());
-                        mExpandedView.debug();
-
-                        mTrackingView.getLocationOnScreen(mAbsPos);
-                        Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
-                                + ") " + mTrackingView.getWidth() + "x"
-                                + mTrackingView.getHeight());
-                        mTrackingView.debug();
+                        Slog.d(TAG, "mStatusIcons:");
+                        mStatusIcons.debug();
                     }
                 });
         }
+
     }
 
     void onBarViewAttached() {
@@ -1534,4 +1508,3 @@
         }
     };
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index 55d31ec..29df28e 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -30,6 +30,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.Usb;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -70,11 +71,11 @@
     static final boolean localLOGV = false;
 
     /** Used to detect when the USB cable is unplugged, so we can call finish() */
-    private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mUsbStateReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) {
-                handleBatteryChanged(intent);
+            if (intent.getAction().equals(Usb.ACTION_USB_STATE)) {
+                handleUsbStateChanged(intent);
             }
         }
     };
@@ -139,7 +140,7 @@
         super.onResume();
 
         mStorageManager.registerListener(mStorageListener);
-        registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        registerReceiver(mUsbStateReceiver, new IntentFilter(Usb.ACTION_USB_STATE));
         try {
             switchDisplay(mStorageManager.isUsbMassStorageEnabled());
         } catch (Exception ex) {
@@ -151,15 +152,15 @@
     protected void onPause() {
         super.onPause();
         
-        unregisterReceiver(mBatteryReceiver);
+        unregisterReceiver(mUsbStateReceiver);
         if (mStorageManager == null && mStorageListener != null) {
             mStorageManager.unregisterListener(mStorageListener);
         }
     }
 
-    private void handleBatteryChanged(Intent intent) {
-        int pluggedType = intent.getIntExtra("plugged", 0);
-        if (pluggedType == 0) {
+    private void handleUsbStateChanged(Intent intent) {
+        boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+        if (!connected) {
             // It was disconnected from the plug, so finish
             finish();
         }
diff --git a/packages/VpnServices/res/values-fr/strings.xml b/packages/VpnServices/res/values-fr/strings.xml
index 80fefa4..69ba34b 100644
--- a/packages/VpnServices/res/values-fr/strings.xml
+++ b/packages/VpnServices/res/values-fr/strings.xml
@@ -2,7 +2,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4589592829302498102">"Services VPN"</string>
-    <string name="vpn_notification_title_connected" msgid="8598654486956133580">"VPN \n<xliff:g id="PROFILENAME">%s</xliff:g> connecté"</string>
+    <string name="vpn_notification_title_connected" msgid="8598654486956133580">"VPN  <xliff:g id="PROFILENAME">%s</xliff:g> connecté"</string>
     <string name="vpn_notification_title_disconnected" msgid="6216572264382192027">"VPN <xliff:g id="PROFILENAME">%s</xliff:g> déconnecté"</string>
     <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Touchez l\'écran pour vous reconnecter à un VPN."</string>
 </resources>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 274124b..384f527 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -232,8 +232,11 @@
         @Override
         public void handleMotion(MotionEvent event, Runnable finishedCallback) {
             finishedCallback.run();
+            
             synchronized (mLock) {
-                mPointerLocationView.addTouchEvent(event);
+                if (mPointerLocationView != null) {
+                    mPointerLocationView.addTouchEvent(event);
+                }
             }
         }
     };
@@ -283,7 +286,8 @@
     // (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
     int mIncallPowerBehavior;
 
-    int mLandscapeRotation = -1;
+    int mLandscapeRotation = -1; // default landscape rotation
+    int mSeascapeRotation = -1; // "other" landscape rotation, 180 degrees from mLandscapeRotation
     int mPortraitRotation = -1;
 
     // Nothing to see here, move along...
@@ -353,9 +357,12 @@
             return true;
         }
         // The user preference says we can rotate, and the app is willing to rotate.
+        // Note we include SCREEN_ORIENTATION_LANDSCAPE since we can use the sensor to choose
+        // between the two possible landscape rotations.
         if (mAccelerometerDefault != 0 &&
                 (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
-                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
+                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
             return true;
         }
         // We're in a dock that has a rotation affinity, an the app is willing to rotate.
@@ -364,7 +371,8 @@
             // Note we override the nosensor flag here.
             if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
                     || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
-                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                 return true;
             }
         }
@@ -2050,20 +2058,20 @@
             if (d.getWidth() > d.getHeight()) {
                 mPortraitRotation = Surface.ROTATION_90;
                 mLandscapeRotation = Surface.ROTATION_0;
+                mSeascapeRotation = Surface.ROTATION_180;
             } else {
                 mPortraitRotation = Surface.ROTATION_0;
                 mLandscapeRotation = Surface.ROTATION_90;
+                mSeascapeRotation = Surface.ROTATION_270;
             }
         }
 
         synchronized (mLock) {
-            switch (orientation) {
-                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
-                    //always return landscape if orientation set to landscape
-                    return mLandscapeRotation;
-                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
-                    //always return portrait if orientation set to portrait
-                    return mPortraitRotation;
+            if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
+                //always return portrait if orientation set to portrait
+                return mPortraitRotation;
+            } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+                return getCurrentLandscapeRotation(lastRotation);
             }
             // case for nosensor meaning ignore sensor and consider only lid
             // or orientation sensor disabled
@@ -2083,6 +2091,26 @@
         }
     }
 
+    private int getCurrentLandscapeRotation(int lastRotation) {
+        // landscape-only apps can take either landscape rotation
+        if (useSensorForOrientationLp(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
+            int sensorRotation = mOrientationListener.getCurrentRotation(lastRotation);
+            if (isLandscapeOrSeascape(sensorRotation)) {
+                return sensorRotation;
+            }
+        }
+        // try to preserve the old rotation if it was landscape
+        if (isLandscapeOrSeascape(lastRotation)) {
+            return lastRotation;
+        }
+        // default to one of the two landscape rotations
+        return mLandscapeRotation;
+    }
+
+    private boolean isLandscapeOrSeascape(int sensorRotation) {
+        return sensorRotation == mLandscapeRotation || sensorRotation == mSeascapeRotation;
+    }
+
     public boolean detectSafeMode() {
         try {
             int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 886c25b..8a732ed 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -4653,17 +4653,44 @@
         goto Exit;
     }
 
+    // check audio settings permission for global effects
+    if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) {
+        lStatus = PERMISSION_DENIED;
+        goto Exit;
+    }
+
+    // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
+    // that can only be created by audio policy manager (running in same process)
+    if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) {
+        lStatus = PERMISSION_DENIED;
+        goto Exit;
+    }
+
+    // check recording permission for visualizer
+    if ((memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
+         memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) &&
+        !recordingAllowed()) {
+        lStatus = PERMISSION_DENIED;
+        goto Exit;
+    }
+
+    if (output == 0) {
+        if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
+            // output must be specified by AudioPolicyManager when using session
+            // AudioSystem::SESSION_OUTPUT_STAGE
+            lStatus = BAD_VALUE;
+            goto Exit;
+        } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+            // if the output returned by getOutputForEffect() is removed before we lock the
+            // mutex below, the call to checkPlaybackThread_l(output) below will detect it
+            // and we will exit safely
+            output = AudioSystem::getOutputForEffect(&desc);
+        }
+    }
+
     {
         Mutex::Autolock _l(mLock);
 
-        // check recording permission for visualizer
-        if (memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
-            memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) {
-            if (!recordingAllowed()) {
-                lStatus = PERMISSION_DENIED;
-                goto Exit;
-            }
-        }
 
         if (!EffectIsNullUuid(&pDesc->uuid)) {
             // if uuid is specified, request effect descriptor
@@ -4727,45 +4754,29 @@
             goto Exit;
         }
 
-        // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
-        // that can only be created by audio policy manager (running in same process)
-        if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE &&
-                getpid() != pid) {
-            lStatus = INVALID_OPERATION;
-            goto Exit;
-        }
-
         // return effect descriptor
         memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
 
         // If output is not specified try to find a matching audio session ID in one of the
         // output threads.
-        // TODO: allow attachment of effect to inputs
+        // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX
+        // because of code checking output when entering the function.
         if (output == 0) {
-            if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
-                // output must be specified by AudioPolicyManager when using session
-                // AudioSystem::SESSION_OUTPUT_STAGE
-                lStatus = BAD_VALUE;
-                goto Exit;
-            } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
-                output = AudioSystem::getOutputForEffect(&desc);
-                LOGV("createEffect() got output %d for effect %s", output, desc.name);
-            } else {
-                 // look for the thread where the specified audio session is present
-                for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
-                    if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
-                        output = mPlaybackThreads.keyAt(i);
-                        break;
-                    }
-                }
-                // If no output thread contains the requested session ID, default to
-                // first output. The effect chain will be moved to the correct output
-                // thread when a track with the same session ID is created
-                if (output == 0 && mPlaybackThreads.size()) {
-                    output = mPlaybackThreads.keyAt(0);
+             // look for the thread where the specified audio session is present
+            for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+                if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
+                    output = mPlaybackThreads.keyAt(i);
+                    break;
                 }
             }
+            // If no output thread contains the requested session ID, default to
+            // first output. The effect chain will be moved to the correct output
+            // thread when a track with the same session ID is created
+            if (output == 0 && mPlaybackThreads.size()) {
+                output = mPlaybackThreads.keyAt(0);
+            }
         }
+        LOGV("createEffect() got output %d for effect %s", output, desc.name);
         PlaybackThread *thread = checkPlaybackThread_l(output);
         if (thread == NULL) {
             LOGE("createEffect() unknown output thread");
@@ -4773,6 +4784,8 @@
             goto Exit;
         }
 
+        // TODO: allow attachment of effect to inputs
+
         wclient = mClients.valueFor(pid);
 
         if (wclient != NULL) {
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index ea2c5d4..58209fd 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -319,6 +319,7 @@
     // Callback is disabled by default
     mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
     mOrientation = 0;
+    mOrientationChanged = false;
     cameraService->setCameraBusy(cameraId);
     cameraService->loadSound();
     LOG1("Client::Client X (pid %d)", callingPid);
@@ -496,6 +497,7 @@
             // Force the destruction of any previous overlay
             sp<Overlay> dummy;
             mHardware->setOverlay(dummy);
+            mOverlayRef = 0;
         } else {
             mSurface->unregisterBuffers();
         }
@@ -539,11 +541,12 @@
     CameraParameters params(mHardware->getParameters());
     params.getPreviewSize(&w, &h);
 
-    if (w != mOverlayW || h != mOverlayH) {
+    if (w != mOverlayW || h != mOverlayH || mOrientationChanged) {
         // Force the destruction of any previous overlay
         sp<Overlay> dummy;
         mHardware->setOverlay(dummy);
         mOverlayRef = 0;
+        mOrientationChanged = false;
     }
 
     status_t result = NO_ERROR;
@@ -810,6 +813,7 @@
 
 status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
     LOG1("sendCommand (pid %d)", getCallingPid());
+    int orientation;
     Mutex::Autolock lock(mLock);
     status_t result = checkPidAndHardware();
     if (result != NO_ERROR) return result;
@@ -821,20 +825,24 @@
         }
         switch (arg1) {
             case 0:
-                mOrientation = ISurface::BufferHeap::ROT_0;
+                orientation = ISurface::BufferHeap::ROT_0;
                 break;
             case 90:
-                mOrientation = ISurface::BufferHeap::ROT_90;
+                orientation = ISurface::BufferHeap::ROT_90;
                 break;
             case 180:
-                mOrientation = ISurface::BufferHeap::ROT_180;
+                orientation = ISurface::BufferHeap::ROT_180;
                 break;
             case 270:
-                mOrientation = ISurface::BufferHeap::ROT_270;
+                orientation = ISurface::BufferHeap::ROT_270;
                 break;
             default:
                 return BAD_VALUE;
         }
+        if (mOrientation != orientation) {
+            mOrientation = orientation;
+            if (mOverlayRef != 0) mOrientationChanged = true;
+        }
         return OK;
     }
 
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 0d69836..8f0ed75 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -164,7 +164,9 @@
         int                             mOverlayW;
         int                             mOverlayH;
         int                             mPreviewCallbackFlag;
-        int                             mOrientation;
+        int                             mOrientation;     // Current display orientation
+        // True if display orientation has been changed. This is only used in overlay.
+        int                             mOrientationChanged;
 
         // Ensures atomicity among the public methods
         mutable Mutex                   mLock;
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c5505d1..b8b9e53 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -48,6 +48,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.GregorianCalendar;
 import java.util.List;
 
 /**
@@ -100,6 +101,64 @@
     private boolean mTestMode;
     private static ConnectivityService sServiceInstance;
 
+    private static final int ENABLED  = 1;
+    private static final int DISABLED = 0;
+
+    // Share the event space with NetworkStateTracker (which can't see this
+    // internal class but sends us events).  If you change these, change
+    // NetworkStateTracker.java too.
+    private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+    private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+
+    /**
+     * used internally as a delayed event to make us switch back to the
+     * default network
+     */
+    private static final int EVENT_RESTORE_DEFAULT_NETWORK =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 1;
+
+    /**
+     * used internally to change our mobile data enabled flag
+     */
+    private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 2;
+
+    /**
+     * used internally to change our network preference setting
+     * arg1 = networkType to prefer
+     */
+    private static final int EVENT_SET_NETWORK_PREFERENCE =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 3;
+
+    /**
+     * used internally to synchronize inet condition reports
+     * arg1 = networkType
+     * arg2 = condition (0 bad, 100 good)
+     */
+    private static final int EVENT_INET_CONDITION_CHANGE =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 4;
+
+    /**
+     * used internally to mark the end of inet condition hold periods
+     * arg1 = networkType
+     */
+    private static final int EVENT_INET_CONDITION_HOLD_END =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 5;
+
+    /**
+     * used internally to set the background data preference
+     * arg1 = TRUE for enabled, FALSE for disabled
+     */
+    private static final int EVENT_SET_BACKGROUND_DATA =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 6;
+
+    /**
+     * used internally to set enable/disable cellular data
+     * arg1 = ENBALED or DISABLED
+     */
+    private static final int EVENT_SET_MOBILE_DATA =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+
     private Handler mHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
@@ -109,6 +168,10 @@
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
 
+    // used in DBG mode to track inet condition reports
+    private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
+    private ArrayList mInetLog;
+
     private static class NetworkAttributes {
         /**
          * Class for holding settings read from resources.
@@ -329,6 +392,9 @@
                                   mTethering.getTetherableWifiRegexs().length != 0) &&
                                  mTethering.getUpstreamIfaceRegexs().length != 0);
 
+        if (DBG) {
+            mInetLog = new ArrayList();
+        }
     }
 
 
@@ -336,28 +402,34 @@
      * Sets the preferred network.
      * @param preference the new preference
      */
-    public synchronized void setNetworkPreference(int preference) {
+    public void setNetworkPreference(int preference) {
         enforceChangePermission();
-        if (ConnectivityManager.isNetworkTypeValid(preference) &&
-                mNetAttributes[preference] != null &&
-                mNetAttributes[preference].isDefault()) {
-            if (mNetworkPreference != preference) {
-                persistNetworkPreference(preference);
-                mNetworkPreference = preference;
-                enforcePreference();
-            }
-        }
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
     }
 
     public int getNetworkPreference() {
         enforceAccessPermission();
-        return mNetworkPreference;
+        int preference;
+        synchronized(this) {
+            preference = mNetworkPreference;
+        }
+        return preference;
     }
 
-    private void persistNetworkPreference(int networkPreference) {
-        final ContentResolver cr = mContext.getContentResolver();
-        Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
-                networkPreference);
+    private void handleSetNetworkPreference(int preference) {
+        if (ConnectivityManager.isNetworkTypeValid(preference) &&
+                mNetAttributes[preference] != null &&
+                mNetAttributes[preference].isDefault()) {
+            if (mNetworkPreference != preference) {
+                final ContentResolver cr = mContext.getContentResolver();
+                Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, preference);
+                synchronized(this) {
+                    mNetworkPreference = preference;
+                }
+                enforcePreference();
+            }
+        }
     }
 
     private int getPersistedNetworkPreference() {
@@ -578,8 +650,7 @@
                         mNetRequestersPids[usedNetworkType].add(currentPid);
                     }
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
                         f), getRestoreDefaultNetworkDelay());
 
 
@@ -605,8 +676,7 @@
                 synchronized(this) {
                     mFeatureUsers.add(f);
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
                         f), getRestoreDefaultNetworkDelay());
 
                 return network.startUsingNetworkFeature(feature,
@@ -776,15 +846,18 @@
                 android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
                 "ConnectivityService");
 
-        if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA,
+                (allowBackgroundDataUsage ? ENABLED : DISABLED), 0));
+    }
 
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.BACKGROUND_DATA,
-                allowBackgroundDataUsage ? 1 : 0);
-
-        Intent broadcast = new Intent(
-                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-        mContext.sendBroadcast(broadcast);
+    private void handleSetBackgroundData(boolean enabled) {
+        if (enabled != getBackgroundDataSetting()) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0);
+            Intent broadcast = new Intent(
+                    ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+            mContext.sendBroadcast(broadcast);
+        }
     }
 
     /**
@@ -801,10 +874,15 @@
     /**
      * @see ConnectivityManager#setMobileDataEnabled(boolean)
      */
-    public synchronized void setMobileDataEnabled(boolean enabled) {
+    public void setMobileDataEnabled(boolean enabled) {
         enforceChangePermission();
         if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")");
 
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
+            (enabled ? ENABLED : DISABLED), 0));
+    }
+
+    private void handleSetMobileData(boolean enabled) {
         if (getMobileDataEnabled() == enabled) return;
 
         Settings.Secure.putInt(mContext.getContentResolver(),
@@ -812,7 +890,9 @@
 
         if (enabled) {
             if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-                if (DBG) Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+                if (DBG) {
+                    Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+                }
                 mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
             }
         } else {
@@ -912,11 +992,19 @@
             newNet = tryFailover(prevNetType);
             if (newNet != null) {
                 NetworkInfo switchTo = newNet.getNetworkInfo();
+                if (!switchTo.isConnected()) {
+                    // if the other net is connected they've already reset this and perhaps even gotten
+                    // a positive report we don't want to overwrite, but if not we need to clear this now
+                    // to turn our cellular sig strength white
+                    mDefaultInetConditionPublished = 0;
+                }
                 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
             } else {
+                mDefaultInetConditionPublished = 0; // we're not connected anymore
                 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
             }
         }
+        intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
         // do this before we broadcast the change
         handleConnectivityChange(prevNetType);
 
@@ -1075,12 +1163,20 @@
             newNet = tryFailover(info.getType());
             if (newNet != null) {
                 NetworkInfo switchTo = newNet.getNetworkInfo();
+                if (!switchTo.isConnected()) {
+                    // if the other net is connected they've already reset this and perhaps even gotten
+                    // a positive report we don't want to overwrite, but if not we need to clear this now
+                    // to turn our cellular sig strength white
+                    mDefaultInetConditionPublished = 0;
+                }
                 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
             } else {
+                mDefaultInetConditionPublished = 0;
                 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
             }
         }
 
+        intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
         sendStickyBroadcast(intent);
         /*
          * If the failover network is already connected, then immediately send
@@ -1365,6 +1461,14 @@
         pw.println();
 
         mTethering.dump(fd, pw, args);
+
+        if (mInetLog != null) {
+            pw.println();
+            pw.println("Inet condition reports:");
+            for(int i = 0; i < mInetLog.size(); i++) {
+                pw.println(mInetLog.get(i));
+            }
+        }
     }
 
     // must be stateless - things change under us.
@@ -1454,74 +1558,42 @@
                 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
                     // fill me in
                     break;
-                case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
+                case EVENT_RESTORE_DEFAULT_NETWORK:
                     FeatureUser u = (FeatureUser)msg.obj;
                     u.expire();
                     break;
-                case NetworkStateTracker.EVENT_INET_CONDITION_CHANGE:
-                    if (DBG) {
-                        Slog.d(TAG, "Inet connectivity change, net=" +
-                                msg.arg1 + ", condition=" + msg.arg2 +
-                                ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
-                    }
-                    if (mActiveDefaultNetwork == -1) {
-                        if (DBG) Slog.d(TAG, "no active default network - aborting");
-                        break;
-                    }
-                    if (mActiveDefaultNetwork != msg.arg1) {
-                        if (DBG) Slog.d(TAG, "given net not default - aborting");
-                        break;
-                    }
-                    mDefaultInetCondition = msg.arg2;
-                    int delay;
-                    if (mInetConditionChangeInFlight == false) {
-                        if (DBG) Slog.d(TAG, "starting a change hold");
-                        // setup a new hold to debounce this
-                        if (mDefaultInetCondition > 50) {
-                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
-                        } else {
-                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
-                        }
-                        mInetConditionChangeInFlight = true;
-                        sendMessageDelayed(obtainMessage(
-                                NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END,
-                                mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
-                    } else {
-                        // we've set the new condition, when this hold ends that will get
-                        // picked up
-                        if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
-                    }
+                case EVENT_INET_CONDITION_CHANGE:
+                {
+                    int netType = msg.arg1;
+                    int condition = msg.arg2;
+                    handleInetConditionChange(netType, condition);
                     break;
-                case NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END:
-                    if (DBG) {
-                        Slog.d(TAG, "Inet hold end, net=" + msg.arg1 +
-                                ", condition =" + mDefaultInetCondition +
-                                ", published condition =" + mDefaultInetConditionPublished);
-                    }
-                    mInetConditionChangeInFlight = false;
-
-                    if (mActiveDefaultNetwork == -1) {
-                        if (DBG) Slog.d(TAG, "no active default network - aborting");
-                        break;
-                    }
-                    if (mDefaultConnectionSequence != msg.arg2) {
-                        if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
-                        break;
-                    }
-                    if (mDefaultInetConditionPublished == mDefaultInetCondition) {
-                        if (DBG) Slog.d(TAG, "no change in condition - aborting");
-                        break;
-                    }
-                    NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
-                    if (networkInfo.isConnected() == false) {
-                        if (DBG) Slog.d(TAG, "default network not connected - aborting");
-                        break;
-                    }
-                    mDefaultInetConditionPublished = mDefaultInetCondition;
-                    sendInetConditionBroadcast(networkInfo);
+                }
+                case EVENT_INET_CONDITION_HOLD_END:
+                {
+                    int netType = msg.arg1;
+                    int sequence = msg.arg2;
+                    handleInetConditionHoldEnd(netType, sequence);
                     break;
+                }
+                case EVENT_SET_NETWORK_PREFERENCE:
+                {
+                    int preference = msg.arg1;
+                    handleSetNetworkPreference(preference);
+                    break;
+                }
+                case EVENT_SET_BACKGROUND_DATA:
+                {
+                    boolean enabled = (msg.arg1 == ENABLED);
+                    handleSetBackgroundData(enabled);
+                    break;
+                }
+                case EVENT_SET_MOBILE_DATA:
+                {
+                    boolean enabled = (msg.arg1 == ENABLED);
+                    handleSetMobileData(enabled);
+                    break;
+                }
             }
         }
     }
@@ -1613,7 +1685,84 @@
                 android.Manifest.permission.STATUS_BAR,
                 "ConnectivityService");
 
+        if (DBG) {
+            int pid = getCallingPid();
+            int uid = getCallingUid();
+            String s = pid + "(" + uid + ") reports inet is " +
+                (percentage > 50 ? "connected" : "disconnected") + " (" + percentage + ") on " +
+                "network Type " + networkType + " at " + GregorianCalendar.getInstance().getTime();
+            mInetLog.add(s);
+            while(mInetLog.size() > INET_CONDITION_LOG_MAX_SIZE) {
+                mInetLog.remove(0);
+            }
+        }
         mHandler.sendMessage(mHandler.obtainMessage(
-            NetworkStateTracker.EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+            EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+    }
+
+    private void handleInetConditionChange(int netType, int condition) {
+        if (DBG) {
+            Slog.d(TAG, "Inet connectivity change, net=" +
+                    netType + ", condition=" + condition +
+                    ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
+        }
+        if (mActiveDefaultNetwork == -1) {
+            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            return;
+        }
+        if (mActiveDefaultNetwork != netType) {
+            if (DBG) Slog.d(TAG, "given net not default - aborting");
+            return;
+        }
+        mDefaultInetCondition = condition;
+        int delay;
+        if (mInetConditionChangeInFlight == false) {
+            if (DBG) Slog.d(TAG, "starting a change hold");
+            // setup a new hold to debounce this
+            if (mDefaultInetCondition > 50) {
+                delay = Settings.Secure.getInt(mContext.getContentResolver(),
+                        Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
+            } else {
+                delay = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
+            }
+            mInetConditionChangeInFlight = true;
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END,
+                    mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
+        } else {
+            // we've set the new condition, when this hold ends that will get
+            // picked up
+            if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
+        }
+    }
+
+    private void handleInetConditionHoldEnd(int netType, int sequence) {
+        if (DBG) {
+            Slog.d(TAG, "Inet hold end, net=" + netType +
+                    ", condition =" + mDefaultInetCondition +
+                    ", published condition =" + mDefaultInetConditionPublished);
+        }
+        mInetConditionChangeInFlight = false;
+
+        if (mActiveDefaultNetwork == -1) {
+            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            return;
+        }
+        if (mDefaultConnectionSequence != sequence) {
+            if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
+            return;
+        }
+        if (mDefaultInetConditionPublished == mDefaultInetCondition) {
+            if (DBG) Slog.d(TAG, "no change in condition - aborting");
+            return;
+        }
+        NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
+        if (networkInfo.isConnected() == false) {
+            if (DBG) Slog.d(TAG, "default network not connected - aborting");
+            return;
+        }
+        mDefaultInetConditionPublished = mDefaultInetCondition;
+        sendInetConditionBroadcast(networkInfo);
+        return;
     }
 }
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 14b7d3e..9829f9a 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.os.IDropBoxManagerService;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -179,7 +180,10 @@
             // the data in uncompressed form.
 
             temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
-            output = new FileOutputStream(temp);
+            int bufferSize = mBlockSize;
+            if (bufferSize > 4096) bufferSize = 4096;
+            if (bufferSize < 512) bufferSize = 512;
+            output = new BufferedOutputStream(new FileOutputStream(temp), bufferSize);
             if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) {
                 output = new GZIPOutputStream(output);
                 flags = flags | DropBoxManager.IS_GZIPPED;
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 81ae26f..0f1fc78 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -139,6 +139,7 @@
             out.println(SystemProperties.get("ro.bootloader"));
             out.println(SystemProperties.get("ro.hardware"));
             out.println(SystemProperties.get("ro.revision"));
+            out.println(new Object().hashCode());
             out.println(System.currentTimeMillis());
             out.println(System.nanoTime());
         } catch (IOException e) {
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 024aec5..29ca9a4 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -53,10 +53,6 @@
     private final Context mContext;
     private final WindowManagerService mWindowManagerService;
     
-    private int mTouchScreenConfig;
-    private int mKeyboardConfig;
-    private int mNavigationConfig;
-    
     private static native void nativeInit(Callbacks callbacks);
     private static native void nativeStart();
     private static native void nativeSetDisplaySize(int displayId, int width, int height);
@@ -79,6 +75,7 @@
     private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
     private static native void nativeSetFocusedApplication(InputApplication application);
     private static native InputDevice nativeGetInputDevice(int deviceId);
+    private static native void nativeGetInputConfiguration(Configuration configuration);
     private static native int[] nativeGetInputDeviceIds();
     private static native String nativeDump();
     
@@ -114,10 +111,6 @@
         
         this.mCallbacks = new Callbacks();
         
-        mTouchScreenConfig = Configuration.TOUCHSCREEN_NOTOUCH;
-        mKeyboardConfig = Configuration.KEYBOARD_NOKEYS;
-        mNavigationConfig = Configuration.NAVIGATION_NONAV;
-        
         init();
     }
     
@@ -154,9 +147,7 @@
             throw new IllegalArgumentException("config must not be null.");
         }
         
-        config.touchscreen = mTouchScreenConfig;
-        config.keyboard = mKeyboardConfig;
-        config.navigation = mNavigationConfig;
+        nativeGetInputConfiguration(config);
     }
     
     /**
@@ -367,12 +358,7 @@
         }
         
         @SuppressWarnings("unused")
-        public void notifyConfigurationChanged(long whenNanos,
-                int touchScreenConfig, int keyboardConfig, int navigationConfig) {
-            mTouchScreenConfig = touchScreenConfig;
-            mKeyboardConfig = keyboardConfig;
-            mNavigationConfig = navigationConfig;
-            
+        public void notifyConfigurationChanged(long whenNanos) {
             mWindowManagerService.sendNewConfiguration();
         }
         
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index f3ce8ba..643b2f5 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -1959,7 +1959,7 @@
 
     // Geocoder
 
-    public boolean geocoderIsImplemented() {
+    public boolean geocoderIsPresent() {
         return mGeocodeProvider != null;
     }
 
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index f3cb9b7..7b68d68 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -180,7 +180,8 @@
         }
     }
 
-    private void sendCommand(String command) {
+    private void sendCommand(String command)
+            throws NativeDaemonConnectorException  {
         sendCommand(command, null);
     }
 
@@ -190,11 +191,13 @@
      * @param command  The command to send to the daemon
      * @param argument The argument to send with the command (or null)
      */
-    private void sendCommand(String command, String argument) {
+    private void sendCommand(String command, String argument)
+            throws NativeDaemonConnectorException  {
         synchronized (this) {
             if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
             if (mOutputStream == null) {
                 Slog.e(TAG, "No connection to daemon", new IllegalStateException());
+                throw new NativeDaemonConnectorException("No output stream!");
             } else {
                 StringBuilder builder = new StringBuilder(command);
                 if (argument != null) {
@@ -224,6 +227,7 @@
 
         while (!complete) {
             try {
+                // TODO - this should not block forever
                 String line = mResponseQueue.take();
                 if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line));
                 String[] tokens = line.split(" ");
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 4a69f20..33b19d6 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -47,6 +47,7 @@
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * @hide
@@ -54,7 +55,7 @@
 class NetworkManagementService extends INetworkManagementService.Stub {
 
     private static final String TAG = "NetworkManagmentService";
-
+    private static final boolean DBG = true;
     private static final String NETD_TAG = "NetdConnector";
 
     class NetdResponseCode {
@@ -86,6 +87,9 @@
      */
     private NativeDaemonConnector mConnector;
 
+    private Thread mThread;
+    private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
+
     private ArrayList<INetworkManagementEventObserver> mObservers;
 
     /**
@@ -93,9 +97,8 @@
      *
      * @param context  Binder context for this service
      */
-    public NetworkManagementService(Context context) {
+    private NetworkManagementService(Context context) {
         mContext = context;
-
         mObservers = new ArrayList<INetworkManagementEventObserver>();
 
         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
@@ -104,8 +107,17 @@
 
         mConnector = new NativeDaemonConnector(
                 new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
-        Thread thread = new Thread(mConnector, NETD_TAG);
-        thread.start();
+        mThread = new Thread(mConnector, NETD_TAG);
+    }
+
+    public static NetworkManagementService create(Context context) throws InterruptedException {
+        NetworkManagementService service = new NetworkManagementService(context);
+        if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
+        service.mThread.start();
+        if (DBG) Slog.d(TAG, "Awaiting socket connection");
+        service.mConnectedSignal.await();
+        if (DBG) Slog.d(TAG, "Connected");
+        return service;
     }
 
     public void registerObserver(INetworkManagementEventObserver obs) {
@@ -157,6 +169,14 @@
         }
     }
 
+    /**
+     * Let us know the daemon is connected
+     */
+    protected void onConnected() {
+        if (DBG) Slog.d(TAG, "onConnected");
+        mConnectedSignal.countDown();
+    }
+
 
     //
     // Netd Callback handling
@@ -164,6 +184,7 @@
 
     class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
         public void onDaemonConnected() {
+            NetworkManagementService.this.onConnected();
             new Thread() {
                 public void run() {
                 }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 72784d5..72daa64 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -91,6 +91,7 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -222,6 +223,9 @@
     // This is the object monitoring the system app dir.
     final FileObserver mSystemInstallObserver;
 
+    // This is the object monitoring the system app dir.
+    final FileObserver mVendorInstallObserver;
+
     // This is the object monitoring mAppInstallDir.
     final FileObserver mAppInstallObserver;
 
@@ -234,6 +238,7 @@
 
     final File mFrameworkDir;
     final File mSystemAppDir;
+    final File mVendorAppDir;
     final File mAppInstallDir;
     final File mDalvikCacheDir;
 
@@ -927,6 +932,14 @@
             scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode);
             
+            // Collect all vendor packages.
+            mVendorAppDir = new File("/vendor/app");
+            mVendorInstallObserver = new AppDirObserver(
+                mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
+            mVendorInstallObserver.startWatching();
+            scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode);
+
             if (mInstaller != null) {
                 if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
                 mInstaller.moveFiles();
@@ -2493,9 +2506,13 @@
     }
 
     private void scanDirLI(File dir, int flags, int scanMode) {
-        Log.d(TAG, "Scanning app dir " + dir);
-
         String[] files = dir.list();
+        if (files == null) {
+            Log.d(TAG, "No files in app dir " + dir);
+            return;
+        }
+
+        Log.d(TAG, "Scanning app dir " + dir);
 
         int i;
         for (i=0; i<files.length; i++) {
@@ -8288,7 +8305,8 @@
             mPastSignatures.clear();
 
             try {
-                FileOutputStream str = new FileOutputStream(mSettingsFilename);
+                BufferedOutputStream str = new BufferedOutputStream(new FileOutputStream(
+                        mSettingsFilename));
 
                 //XmlSerializer serializer = XmlUtils.serializerInstance();
                 XmlSerializer serializer = new FastXmlSerializer();
@@ -8385,7 +8403,7 @@
                 File tempFile = new File(mPackageListFilename.toString() + ".tmp");
                 JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
 
-                str = new FileOutputStream(journal.chooseForWrite());
+                str = new BufferedOutputStream(new FileOutputStream(journal.chooseForWrite()));
                 try {
                     StringBuilder sb = new StringBuilder();
                     for (PackageSetting pkg : mPackages.values()) {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 8ab1bb8..5d32b74 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -25,6 +25,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ContentQueryMap;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -36,6 +37,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Environment;
@@ -109,6 +111,9 @@
     // Cached secure settings; see updateSettingsValues()
     private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
 
+    // Default timeout for screen off, if not found in settings database = 15 seconds.
+    private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;
+
     // flags for setPowerState
     private static final int SCREEN_ON_BIT          = 0x00000001;
     private static final int SCREEN_BRIGHT_BIT      = 0x00000002;
@@ -411,24 +416,28 @@
     }
 
     private class SettingsObserver implements Observer {
-        private int getInt(String name) {
-            return mSettings.getValues(name).getAsInteger(Settings.System.VALUE);
+        private int getInt(String name, int defValue) {
+            ContentValues values = mSettings.getValues(name);
+            Integer iVal = values != null ? values.getAsInteger(Settings.System.VALUE) : null;
+            return iVal != null ? iVal : defValue;
         }
 
         public void update(Observable o, Object arg) {
             synchronized (mLocks) {
-                // STAY_ON_WHILE_PLUGGED_IN
-                mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN);
+                // STAY_ON_WHILE_PLUGGED_IN, default to when plugged into AC
+                mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN,
+                        BatteryManager.BATTERY_PLUGGED_AC);
                 updateWakeLockLocked();
 
-                // SCREEN_OFF_TIMEOUT
-                mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT);
+                // SCREEN_OFF_TIMEOUT, default to 15 seconds
+                mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
 
                  // DIM_SCREEN
                 //mDimScreen = getInt(DIM_SCREEN) != 0;
 
-                // SCREEN_BRIGHTNESS_MODE
-                setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE));
+                // SCREEN_BRIGHTNESS_MODE, default to manual
+                setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE,
+                        Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL));
 
                 // recalculate everything
                 setScreenOffTimeoutsLocked();
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 0c8c2fd..020f9ed 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -80,16 +80,24 @@
         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 11: major faults
         PROC_SPACE_TERM,
         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 13: utime
-        PROC_SPACE_TERM|PROC_OUT_LONG                   // 14: stime
+        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 14: stime
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM,
+        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 21: vsize
     };
 
     static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1;
     static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2;
     static final int PROCESS_FULL_STAT_UTIME = 3;
     static final int PROCESS_FULL_STAT_STIME = 4;
+    static final int PROCESS_FULL_STAT_VSIZE = 5;
 
-    private final String[] mProcessFullStatsStringData = new String[5];
-    private final long[] mProcessFullStatsData = new long[5];
+    private final String[] mProcessFullStatsStringData = new String[6];
+    private final long[] mProcessFullStatsData = new long[6];
 
     private static final int[] SYSTEM_CPU_FORMAT = new int[] {
         PROC_SPACE_TERM|PROC_COMBINE,
@@ -171,6 +179,8 @@
         final ArrayList<Stats> threadStats;
         final ArrayList<Stats> workingThreads;
         
+        public boolean interesting;
+
         public String baseName;
         public String name;
         int nameWidth;
@@ -349,59 +359,62 @@
                         + (parentPid < 0 ? "process" : "thread")
                         + " pid " + pid + ": " + st);
 
-                final long uptime = SystemClock.uptimeMillis();
+                if (st.interesting) {
+                    final long uptime = SystemClock.uptimeMillis();
 
-                final long[] procStats = mProcessStatsData;
-                if (!Process.readProcFile(st.statFile.toString(),
-                        PROCESS_STATS_FORMAT, null, procStats, null)) {
-                    continue;
-                }
-                
-                final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
-                final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
-                final long utime = procStats[PROCESS_STAT_UTIME];
-                final long stime = procStats[PROCESS_STAT_STIME];
-
-                if (utime == st.base_utime && stime == st.base_stime) {
-                    st.rel_utime = 0;
-                    st.rel_stime = 0;
-                    st.rel_minfaults = 0;
-                    st.rel_majfaults = 0;
-                    if (st.active) {
-                        st.active = false;
+                    final long[] procStats = mProcessStatsData;
+                    if (!Process.readProcFile(st.statFile.toString(),
+                            PROCESS_STATS_FORMAT, null, procStats, null)) {
+                        continue;
                     }
-                    continue;
-                }
                     
-                if (!st.active) {
-                    st.active = true;
-                }
+                    final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
+                    final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
+                    final long utime = procStats[PROCESS_STAT_UTIME];
+                    final long stime = procStats[PROCESS_STAT_STIME];
 
-                if (parentPid < 0) {
-                    getName(st, st.cmdlineFile);
-                    if (st.threadStats != null) {
-                        mCurThreadPids = collectStats(st.threadsDir, pid, false,
-                                mCurThreadPids, st.threadStats);
+                    if (utime == st.base_utime && stime == st.base_stime) {
+                        st.rel_utime = 0;
+                        st.rel_stime = 0;
+                        st.rel_minfaults = 0;
+                        st.rel_majfaults = 0;
+                        if (st.active) {
+                            st.active = false;
+                        }
+                        continue;
                     }
+
+                    if (!st.active) {
+                        st.active = true;
+                    }
+
+                    if (parentPid < 0) {
+                        getName(st, st.cmdlineFile);
+                        if (st.threadStats != null) {
+                            mCurThreadPids = collectStats(st.threadsDir, pid, false,
+                                    mCurThreadPids, st.threadStats);
+                        }
+                    }
+
+                    if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
+                            + " utime=" + utime + "-" + st.base_utime
+                            + " stime=" + stime + "-" + st.base_stime
+                            + " minfaults=" + minfaults + "-" + st.base_minfaults
+                            + " majfaults=" + majfaults + "-" + st.base_majfaults);
+
+                    st.rel_uptime = uptime - st.base_uptime;
+                    st.base_uptime = uptime;
+                    st.rel_utime = (int)(utime - st.base_utime);
+                    st.rel_stime = (int)(stime - st.base_stime);
+                    st.base_utime = utime;
+                    st.base_stime = stime;
+                    st.rel_minfaults = (int)(minfaults - st.base_minfaults);
+                    st.rel_majfaults = (int)(majfaults - st.base_majfaults);
+                    st.base_minfaults = minfaults;
+                    st.base_majfaults = majfaults;
+                    st.working = true;
                 }
 
-                if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
-                        + " utime=" + utime + "-" + st.base_utime
-                        + " stime=" + stime + "-" + st.base_stime
-                        + " minfaults=" + minfaults + "-" + st.base_minfaults
-                        + " majfaults=" + majfaults + "-" + st.base_majfaults);
-
-                st.rel_uptime = uptime - st.base_uptime;
-                st.base_uptime = uptime;
-                st.rel_utime = (int)(utime - st.base_utime);
-                st.rel_stime = (int)(stime - st.base_stime);
-                st.base_utime = utime;
-                st.base_stime = stime;
-                st.rel_minfaults = (int)(minfaults - st.base_minfaults);
-                st.rel_majfaults = (int)(majfaults - st.base_majfaults);
-                st.base_minfaults = minfaults;
-                st.base_majfaults = majfaults;
-                st.working = true;
                 continue;
             }
             
@@ -421,12 +434,24 @@
                 if (Process.readProcFile(st.statFile.toString(),
                         PROCESS_FULL_STATS_FORMAT, procStatsString,
                         procStats, null)) {
-                    st.baseName = procStatsString[0];
-                    st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
-                    st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
-                    st.base_utime = procStats[PROCESS_FULL_STAT_UTIME];
-                    st.base_stime = procStats[PROCESS_FULL_STAT_STIME];
+                    // This is a possible way to filter out processes that
+                    // are actually kernel threads...  do we want to?  Some
+                    // of them do use CPU, but there can be a *lot* that are
+                    // not doing anything.
+                    if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
+                        st.interesting = true;
+                        st.baseName = procStatsString[0];
+                        st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
+                        st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
+                        st.base_utime = procStats[PROCESS_FULL_STAT_UTIME];
+                        st.base_stime = procStats[PROCESS_FULL_STAT_STIME];
+                    } else {
+                        Slog.i(TAG, "Skipping kernel process pid " + pid
+                                + " name " + procStatsString[0]);
+                        st.baseName = procStatsString[0];
+                    }
                 } else {
+                    Slog.w(TAG, "Skipping unknown process pid " + pid);
                     st.baseName = "<unknown>";
                     st.base_utime = st.base_stime = 0;
                     st.base_minfaults = st.base_majfaults = 0;
@@ -438,7 +463,7 @@
                         mCurThreadPids = collectStats(st.threadsDir, pid, true,
                                 mCurThreadPids, st.threadStats);
                     }
-                } else {
+                } else if (st.interesting) {
                     st.name = st.baseName;
                     st.nameWidth = onMeasureProcessName(st.name);
                 }
@@ -452,7 +477,7 @@
                 st.rel_minfaults = 0;
                 st.rel_majfaults = 0;
                 st.added = true;
-                if (!first) {
+                if (!first && st.interesting) {
                     st.working = true;
                 }
                 continue;
@@ -624,6 +649,14 @@
         }
     }
 
+    final public int countStats() {
+        return mProcStats.size();
+    }
+
+    final public Stats getStats(int index) {
+        return mProcStats.get(index);
+    }
+
     final public int countWorkingStats() {
         buildWorkingProcs();
         return mWorkingProcs.size();
@@ -788,7 +821,8 @@
 
     private void getName(Stats st, String cmdlineFile) {
         String newName = st.name;
-        if (st.name == null || st.name.equals("app_process")) {
+        if (st.name == null || st.name.equals("app_process")
+                || st.name.equals("<pre-initialized>")) {
             String cmdName = readFile(cmdlineFile, '\0');
             if (cmdName != null && cmdName.length() > 1) {
                 newName = cmdName;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 859de46..a80ab1d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,7 +17,6 @@
 package com.android.server;
 
 import com.android.server.am.ActivityManagerService;
-import com.android.server.sip.SipService;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.SamplingProfilerIntegration;
 
@@ -254,7 +253,8 @@
             try {
                 Slog.i(TAG, "NetworkManagement Service");
                 ServiceManager.addService(
-                        Context.NETWORKMANAGEMENT_SERVICE, new NetworkManagementService(context));
+                        Context.NETWORKMANAGEMENT_SERVICE,
+                        NetworkManagementService.create(context));
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting NetworkManagement Service", e);
             }
@@ -416,16 +416,6 @@
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting DiskStats Service", e);
             }
-
-            try {
-                SipService sipService = SipService.create(context);
-                if (sipService != null) {
-                    Slog.i(TAG, "Sip Service");
-                    ServiceManager.addService("sip", sipService);
-                }
-            } catch (Throwable e) {
-                Slog.e(TAG, "Failure starting SIP Service", e);
-            }
         }
 
         // make sure the ADB_ENABLED setting value matches the secure property value
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 371f22e..3d95bf0 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -145,7 +145,7 @@
      */
     private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
 
-    private static final String WAKELOCK_TAG = "WifiService";
+    private static final String WAKELOCK_TAG = "*wifi*";
 
     /**
      * The maximum amount of time to hold the wake lock after a disconnect
@@ -173,6 +173,7 @@
     private static final int MESSAGE_STOP_ACCESS_POINT  = 7;
     private static final int MESSAGE_SET_CHANNELS       = 8;
     private static final int MESSAGE_ENABLE_NETWORKS    = 9;
+    private static final int MESSAGE_START_SCAN         = 10;
 
 
     private final  WifiHandler mWifiHandler;
@@ -385,23 +386,12 @@
 
     /**
      * see {@link android.net.wifi.WifiManager#startScan()}
-     * @return {@code true} if the operation succeeds
      */
-    public boolean startScan(boolean forceActive) {
+    public void startScan(boolean forceActive) {
         enforceChangePermission();
+        if (mWifiHandler == null) return;
 
-        switch (mWifiStateTracker.getSupplicantState()) {
-            case DISCONNECTED:
-            case INACTIVE:
-            case SCANNING:
-            case DORMANT:
-                break;
-            default:
-                mWifiStateTracker.setScanResultHandling(
-                        WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
-                break;
-        }
-        return mWifiStateTracker.scan(forceActive);
+        Message.obtain(mWifiHandler, MESSAGE_START_SCAN, forceActive ? 1 : 0, 0).sendToTarget();
     }
 
     /**
@@ -1825,6 +1815,7 @@
                 }
             }
             mWifiStateTracker.updateBatteryWorkSourceLocked(mTmpWorkSource);
+            sWakeLock.setWorkSource(mTmpWorkSource);
         }
     }
 
@@ -2000,6 +1991,21 @@
                     mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
                     break;
 
+                case MESSAGE_START_SCAN:
+                    boolean forceActive = (msg.arg1 == 1);
+                    switch (mWifiStateTracker.getSupplicantState()) {
+                        case DISCONNECTED:
+                        case INACTIVE:
+                        case SCANNING:
+                        case DORMANT:
+                            break;
+                        default:
+                            mWifiStateTracker.setScanResultHandling(
+                                    WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
+                            break;
+                    }
+                    mWifiStateTracker.scan(forceActive);
+                    break;
             }
         }
     }
@@ -2203,7 +2209,7 @@
     }
 
     private boolean acquireWifiLockLocked(WifiLock wifiLock) {
-        Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
+        if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
 
         mLocks.addLock(wifiLock);
 
@@ -2273,7 +2279,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/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 9835098..d7a3596 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -3102,8 +3102,11 @@
                 
             } else if (currentConfig != null) {
                 // No obvious action we need to take, but if our current
-                // state mismatches the activity maanager's, update it
+                // state mismatches the activity manager's, update it,
+                // disregarding font scale, which should remain set to
+                // the value of the previous configuration.
                 mTempConfiguration.setToDefaults();
+                mTempConfiguration.fontScale = currentConfig.fontScale;
                 if (computeNewConfigurationLocked(mTempConfiguration)) {
                     if (currentConfig.diff(mTempConfiguration) != 0) {
                         mWaitingForConfig = true;
@@ -5679,9 +5682,12 @@
                 int requestedWidth, int requestedHeight, int viewFlags,
                 boolean insetsPending, Rect outFrame, Rect outContentInsets,
                 Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
-            return relayoutWindow(this, window, attrs,
+            //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());
+            int res = relayoutWindow(this, window, attrs,
                     requestedWidth, requestedHeight, viewFlags, insetsPending,
                     outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
+            //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());
+            return res;
         }
 
         public void setTransparentRegion(IWindow window, Region region) {
@@ -8316,6 +8322,11 @@
             return;
         }
         
+        if (mDisplay == null) {
+            // Not yet initialized, nothing to do.
+            return;
+        }
+
         boolean recoveringMemory = false;
         if (mForceRemoves != null) {
             recoveringMemory = true;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index cf767ca..34753e7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -121,6 +121,8 @@
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -144,6 +146,7 @@
     static final boolean DEBUG_BROADCAST = localLOGV || false;
     static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
     static final boolean DEBUG_SERVICE = localLOGV || false;
+    static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
     static final boolean DEBUG_VISBILITY = localLOGV || false;
     static final boolean DEBUG_PROCESSES = localLOGV || false;
     static final boolean DEBUG_PROVIDER = localLOGV || false;
@@ -152,6 +155,8 @@
     static final boolean DEBUG_RESULTS = localLOGV || false;
     static final boolean DEBUG_BACKUP = localLOGV || false;
     static final boolean DEBUG_CONFIGURATION = localLOGV || false;
+    static final boolean DEBUG_POWER = localLOGV || false;
+    static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
     static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
     
@@ -197,8 +202,16 @@
     // The minimum amount of time between successive GC requests for a process.
     static final int GC_MIN_INTERVAL = 60*1000;
 
-    // The rate at which we check for apps using excessive wake locks -- 15 mins.
-    static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000;
+    // The rate at which we check for apps using excessive power -- 15 mins.
+    static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000;
+
+    // The minimum sample duration we will allow before deciding we have
+    // enough data on wake locks to start killing things.
+    static final int WAKE_LOCK_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
+
+    // The minimum sample duration we will allow before deciding we have
+    // enough data on CPU usage to start killing things.
+    static final int CPU_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
 
     // How long we allow a receiver to run before giving up on it.
     static final int BROADCAST_TIMEOUT = 10*1000;
@@ -552,6 +565,11 @@
     private final StringBuilder mStrictModeBuffer = new StringBuilder();
 
     /**
+     * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+     */
+    private boolean mPendingBroadcastTimeoutMessage;
+
+    /**
      * Intent broadcast that we have tried to start, but are
      * waiting for its application's process to be created.  We only
      * need one (instead of a list) because we always process broadcasts
@@ -561,6 +579,11 @@
     BroadcastRecord mPendingBroadcast = null;
 
     /**
+     * The receiver index that is pending, to restart the broadcast if needed.
+     */
+    int mPendingBroadcastRecvIndex;
+
+    /**
      * Keeps track of all IIntentReceivers that have been registered for
      * broadcasts.  Hash keys are the receiver IBinder, hash value is
      * a ReceiverList.
@@ -747,6 +770,7 @@
     ComponentName mTopComponent;
     String mTopAction;
     String mTopData;
+    boolean mProcessesReady = false;
     boolean mSystemReady = false;
     boolean mBooting = false;
     boolean mWaitingUpdate = false;
@@ -773,9 +797,14 @@
     boolean mDidAppSwitch;
     
     /**
-     * Last time (in realtime) at which we checked for wake lock usage.
+     * Last time (in realtime) at which we checked for power usage.
      */
-    long mLastWakeLockCheckTime;
+    long mLastPowerCheckRealtime;
+
+    /**
+     * Last time (in uptime) at which we checked for power usage.
+     */
+    long mLastPowerCheckUptime;
 
     /**
      * Set while we are wanting to sleep, to prevent any
@@ -964,7 +993,11 @@
                         return;
                     }
                     
-                    broadcastIntentLocked(null, null, new Intent("android.intent.action.ANR"),
+                    Intent intent = new Intent("android.intent.action.ANR");
+                    if (!mProcessesReady) {
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    }
+                    broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null,
                             false, false, MY_PID, Process.SYSTEM_UID);
 
@@ -1042,17 +1075,8 @@
                 processNextBroadcast(true);
             } break;
             case BROADCAST_TIMEOUT_MSG: {
-                if (mDidDexOpt) {
-                    mDidDexOpt = false;
-                    Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
-                    mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT);
-                    return;
-                }
-                // Only process broadcast timeouts if the system is ready. That way
-                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
-                // to do heavy lifting for system up
-                if (mSystemReady) {
-                    broadcastTimeout();
+                synchronized (ActivityManagerService.this) {
+                    broadcastTimeoutLocked(true);
                 }
             } break;
             case SERVICE_TIMEOUT_MSG: {
@@ -1184,12 +1208,10 @@
             } break;
             case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    checkExcessiveWakeLocksLocked(true);
+                    checkExcessivePowerUsageLocked(true);
                     removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                    if (mSleeping) {
-                        Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                        sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
-                    }
+                    Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+                    sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                 }
             } break;
             }
@@ -1383,8 +1405,9 @@
         mBatteryStatsService = new BatteryStatsService(new File(
                 systemDir, "batterystats.bin").toString());
         mBatteryStatsService.getActiveStatistics().readLocked();
-        mBatteryStatsService.getActiveStatistics().writeLocked();
-        mOnBattery = mBatteryStatsService.getActiveStatistics().getIsOnBattery();
+        mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
+        mOnBattery = DEBUG_POWER ? true
+                : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
         mBatteryStatsService.getActiveStatistics().setCallback(this);
         
         mUsageStatsService = new UsageStatsService(new File(
@@ -1504,10 +1527,12 @@
                             int perc = bstats.startAddingCpuLocked();
                             int totalUTime = 0;
                             int totalSTime = 0;
-                            final int N = mProcessStats.countWorkingStats();
+                            final int N = mProcessStats.countStats();
                             for (int i=0; i<N; i++) {
-                                ProcessStats.Stats st
-                                        = mProcessStats.getWorkingStats(i);
+                                ProcessStats.Stats st = mProcessStats.getStats(i);
+                                if (!st.working) {
+                                    continue;
+                                }
                                 ProcessRecord pr = mPidsSelfLocked.get(st.pid);
                                 int otherUTime = (st.rel_utime*perc)/100;
                                 int otherSTime = (st.rel_stime*perc)/100;
@@ -1518,6 +1543,7 @@
                                     ps.addCpuTimeLocked(st.rel_utime-otherUTime,
                                             st.rel_stime-otherSTime);
                                     ps.addSpeedStepTimes(cpuSpeedTimes);
+                                    pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
                                 } else {
                                     BatteryStatsImpl.Uid.Proc ps =
                                             bstats.getProcessStatsLocked(st.name, st.pid);
@@ -1536,7 +1562,7 @@
 
                 if (mLastWriteTime < (now-BATTERY_STATS_TIME)) {
                     mLastWriteTime = now;
-                    mBatteryStatsService.getActiveStatistics().writeLocked();
+                    mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
                 }
             }
         }
@@ -1554,7 +1580,7 @@
         updateCpuStatsNow();
         synchronized (this) {
             synchronized(mPidsSelfLocked) {
-                mOnBattery = onBattery;
+                mOnBattery = DEBUG_POWER ? true : onBattery;
             }
         }
     }
@@ -1717,10 +1743,12 @@
             if (!knownToBeDead || app.thread == null) {
                 // We already have the app running, or are waiting for it to
                 // come up (we have a pid but not yet its thread), so keep it.
+                if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
                 return app;
             } else {
                 // An application record is attached to a previous process,
                 // clean it up now.
+                if (DEBUG_PROCESSES) Slog.v(TAG, "App died: " + app);
                 handleAppDiedLocked(app, true);
             }
         }
@@ -1732,6 +1760,8 @@
             // If we are in the background, then check to see if this process
             // is bad.  If so, we will just silently fail.
             if (mBadProcesses.get(info.processName, info.uid) != null) {
+                if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+                        + "/" + info.processName);
                 return null;
             }
         } else {
@@ -1739,6 +1769,8 @@
             // crash count so that we won't make it bad until they see at
             // least one crash dialog again, and make the process good again
             // if it had been bad.
+            if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+                    + "/" + info.processName);
             mProcessCrashTimes.remove(info.processName, info.uid);
             if (mBadProcesses.get(info.processName, info.uid) != null) {
                 EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
@@ -1760,12 +1792,13 @@
 
         // If the system is not ready yet, then hold off on starting this
         // process until it is.
-        if (!mSystemReady
+        if (!mProcessesReady
                 && !isAllowedWhileBooting(info)
                 && !allowWhileBooting) {
             if (!mProcessesOnHold.contains(app)) {
                 mProcessesOnHold.add(app);
             }
+            if (DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
             return app;
         }
 
@@ -1787,6 +1820,8 @@
             app.pid = 0;
         }
 
+        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+                "startProcessLocked removing on hold: " + app);
         mProcessesOnHold.remove(app);
 
         updateCpuStats();
@@ -2766,11 +2801,36 @@
         }
     }
 
+    private final class AppNotResponding implements Runnable {
+        private final ProcessRecord mApp;
+        private final String mAnnotation;
+
+        public AppNotResponding(ProcessRecord app, String annotation) {
+            mApp = app;
+            mAnnotation = annotation;
+        }
+
+        @Override
+        public void run() {
+            appNotResponding(mApp, null, null, mAnnotation);
+        }
+    }
+
     final void appNotResponding(ProcessRecord app, ActivityRecord activity,
             ActivityRecord parent, final String annotation) {
         ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
         SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
 
+        if (mController != null) {
+            try {
+                // 0 == continue, -1 = kill process immediately
+                int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
+                if (res < 0 && app.pid != MY_PID) Process.killProcess(app.pid);
+            } catch (RemoteException e) {
+                mController = null;
+            }
+        }
+
         long anrTime = SystemClock.uptimeMillis();
         if (MONITOR_CPU_USAGE) {
             updateCpuStatsNow();
@@ -2821,10 +2881,6 @@
             }
         }
 
-        final ProcessStats processStats = new ProcessStats(true);
-
-        File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids);
-
         // Log the ANR to the main log.
         StringBuilder info = mStringBuilder;
         info.setLength(0);
@@ -2840,6 +2896,10 @@
             info.append("Parent: ").append(parent.shortComponentName).append("\n");
         }
 
+        final ProcessStats processStats = new ProcessStats(true);
+
+        File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids);
+
         String cpuInfo = null;
         if (MONITOR_CPU_USAGE) {
             updateCpuStatsNow();
@@ -3040,11 +3100,8 @@
                 Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
                         Uri.fromParts("package", packageName, null));
                 intent.putExtra(Intent.EXTRA_UID, pkgUid);
-                synchronized (this) {
-                    broadcastIntentLocked(null, null, intent,
-                            null, null, 0, null, null, null,
-                            false, false, MY_PID, Process.SYSTEM_UID);
-                }
+                broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
+                        null, null, 0, null, null, null, false, false);
             } catch (RemoteException e) {
             }
         } finally {
@@ -3148,6 +3205,7 @@
 
     public void closeSystemDialogs(String reason) {
         Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         if (reason != null) {
             intent.putExtra("reason", reason);
         }
@@ -3225,6 +3283,9 @@
         forceStopPackageLocked(packageName, uid, false, false, true);
         Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
                 Uri.fromParts("package", packageName, null));
+        if (!mProcessesReady) {
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        }
         intent.putExtra(Intent.EXTRA_UID, uid);
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null,
@@ -3427,6 +3488,8 @@
             }
             if (mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid) {
                 Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
+                mPendingBroadcast.state = BroadcastRecord.IDLE;
+                mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
                 mPendingBroadcast = null;
                 scheduleBroadcastsLocked();
             }
@@ -3502,7 +3565,7 @@
 
         mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
 
-        boolean normalMode = mSystemReady || isAllowedWhileBooting(app.info);
+        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
         List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
 
         if (!normalMode) {
@@ -3561,6 +3624,8 @@
 
         // Remove this record from the list of starting applications.
         mPersistentStartingProcesses.remove(app);
+        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+                "Attach application locked removing on hold: " + app);
         mProcessesOnHold.remove(app);
 
         boolean badApp = false;
@@ -3619,7 +3684,7 @@
                 Slog.w(TAG, "Exception in new application when starting receiver "
                       + br.curComponent.flattenToShortString(), e);
                 badApp = true;
-                logBroadcastReceiverDiscard(br);
+                logBroadcastReceiverDiscardLocked(br);
                 finishReceiverLocked(br.receiver, br.resultCode, br.resultData,
                         br.resultExtras, br.resultAbort, true);
                 scheduleBroadcastsLocked();
@@ -3702,14 +3767,16 @@
                 ArrayList<ProcessRecord> procs =
                     new ArrayList<ProcessRecord>(mProcessesOnHold);
                 for (int ip=0; ip<NP; ip++) {
-                    this.startProcessLocked(procs.get(ip), "on-hold", null);
+                    if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
+                            + procs.get(ip));
+                    startProcessLocked(procs.get(ip), "on-hold", null);
                 }
             }
             
             if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
                 // Start looking for apps that are abusing wake locks.
                 Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+                mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                 // Tell anyone interested that we are done booting!
                 SystemProperties.set("sys.boot_completed", "1");
                 broadcastIntentLocked(null, null,
@@ -4151,29 +4218,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,
@@ -4266,7 +4379,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);
@@ -4300,7 +4413,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);
@@ -4468,7 +4581,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)) {
@@ -5253,7 +5366,7 @@
                     throw new SecurityException(msg);
                 }
 
-                if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
+                if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
                         && !cpi.processName.equals("system")) {
                     // If this content provider does not run in the system
                     // process, and the system is not yet ready to run other
@@ -5525,6 +5638,38 @@
         }
     }
 
+    /**
+     * Allows app to retrieve the MIME type of a URI without having permission
+     * to access its content provider.
+     *
+     * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+     *
+     * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+     *     src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+     */
+    public String getProviderMimeType(Uri uri) {
+        final String name = uri.getAuthority();
+        final long ident = Binder.clearCallingIdentity();
+        ContentProviderHolder holder = null;
+
+        try {
+            holder = getContentProviderExternal(name);
+            if (holder != null) {
+                return holder.provider.getType(uri);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Content provider dead retrieving " + uri, e);
+            return null;
+        } finally {
+            if (holder != null) {
+                removeContentProviderExternal(name);
+            }
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        return null;
+    }
+
     // =========================================================
     // GLOBAL MANAGEMENT
     // =========================================================
@@ -5622,10 +5767,10 @@
             }
 
             // Initialize the wake times of all processes.
-            checkExcessiveWakeLocksLocked(false);
+            checkExcessivePowerUsageLocked(false);
             mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
             Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-            mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+            mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
         }
     }
 
@@ -5675,7 +5820,6 @@
             mWindowManager.setEventDispatching(true);
             mSleeping = false;
             mMainStack.resumeTopActivityLocked(null);
-            mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
         }
     }
 
@@ -6040,6 +6184,11 @@
                     Slog.i(TAG, "Removing system update proc: " + proc);
                     removeProcessLocked(proc, true);
                 }
+
+                // Now that we have cleaned up any update processes, we
+                // are ready to start launching real processes and know that
+                // we won't trample on them any more.
+                mProcessesReady = true;
             }
         }
         
@@ -6289,7 +6438,7 @@
             // The current broadcast is waiting for this app's receiver
             // to be finished.  Looks like that's not going to happen, so
             // let the broadcast continue.
-            logBroadcastReceiverDiscard(r);
+            logBroadcastReceiverDiscardLocked(r);
             finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
                     r.resultExtras, r.resultAbort, true);
             reschedule = true;
@@ -6298,7 +6447,7 @@
         if (r != null && r.curApp == app) {
             if (DEBUG_BROADCAST) Slog.v(TAG,
                     "skip & discard pending app " + r);
-            logBroadcastReceiverDiscard(r);
+            logBroadcastReceiverDiscardLocked(r);
             finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
                     r.resultExtras, r.resultAbort, true);
             reschedule = true;
@@ -6967,12 +7116,13 @@
                 pw.println("Activity manager dump options:");
                 pw.println("  [-a] [-h] [cmd] ...");
                 pw.println("  cmd may be one of:");
-                pw.println("    activities: activity stack state");
-                pw.println("    broadcasts: broadcast state");
-                pw.println("    intents: pending intent state");
-                pw.println("    processes: process state");
-                pw.println("    providers: content provider state");
-                pw.println("    services: service state");
+                pw.println("    a[ctivities]: activity stack state");
+                pw.println("    b[roadcasts]: broadcast state");
+                pw.println("    i[ntents]: pending intent state");
+                pw.println("    p[rocesses]: process state");
+                pw.println("    o[om]: out of memory management");
+                pw.println("    prov[iders]: content provider state");
+                pw.println("    s[ervices]: service state");
                 pw.println("    service [name]: service client-side state");
                 return;
             } else {
@@ -7004,6 +7154,11 @@
                     dumpProcessesLocked(fd, pw, args, opti, true);
                 }
                 return;
+            } else if ("oom".equals(cmd) || "o".equals(cmd)) {
+                synchronized (this) {
+                    dumpOomLocked(fd, pw, args, opti, true);
+                }
+                return;
             } else if ("providers".equals(cmd) || "prov".equals(cmd)) {
                 synchronized (this) {
                     dumpProvidersLocked(fd, pw, args, opti, true);
@@ -7119,7 +7274,7 @@
         
         return true;
     }
-        
+
     boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll) {
         boolean needSep = false;
@@ -7149,8 +7304,8 @@
             if (needSep) pw.println(" ");
             needSep = true;
             pw.println("  Running processes (most recent first):");
-            dumpProcessList(pw, this, mLruProcesses, "    ",
-                    "Proc", "PERS", true);
+            dumpProcessOomList(pw, this, mLruProcesses, "    ",
+                    "Proc", "PERS", false);
             needSep = true;
         }
 
@@ -7181,7 +7336,7 @@
             needSep = true;
             pw.println("  Persisent processes that are starting:");
             dumpProcessList(pw, this, mPersistentStartingProcesses, "    ",
-                    "Starting Norm", "Restarting PERS", false);
+                    "Starting Norm", "Restarting PERS");
         }
 
         if (mStartingProcesses.size() > 0) {
@@ -7189,7 +7344,7 @@
             needSep = true;
             pw.println("  Processes that are starting:");
             dumpProcessList(pw, this, mStartingProcesses, "    ",
-                    "Starting Norm", "Starting PERS", false);
+                    "Starting Norm", "Starting PERS");
         }
 
         if (mRemovedProcesses.size() > 0) {
@@ -7197,7 +7352,7 @@
             needSep = true;
             pw.println("  Processes that are being removed:");
             dumpProcessList(pw, this, mRemovedProcesses, "    ",
-                    "Removed Norm", "Removed PERS", false);
+                    "Removed Norm", "Removed PERS");
         }
         
         if (mProcessesOnHold.size() > 0) {
@@ -7205,26 +7360,10 @@
             needSep = true;
             pw.println("  Processes that are on old until the system is ready:");
             dumpProcessList(pw, this, mProcessesOnHold, "    ",
-                    "OnHold Norm", "OnHold PERS", false);
+                    "OnHold Norm", "OnHold PERS");
         }
 
-        if (mProcessesToGc.size() > 0) {
-            if (needSep) pw.println(" ");
-            needSep = true;
-            pw.println("  Processes that are waiting to GC:");
-            long now = SystemClock.uptimeMillis();
-            for (int i=0; i<mProcessesToGc.size(); i++) {
-                ProcessRecord proc = mProcessesToGc.get(i);
-                pw.print("    Process "); pw.println(proc);
-                pw.print("      lowMem="); pw.print(proc.reportLowMemory);
-                        pw.print(", last gced=");
-                        pw.print(now-proc.lastRequestedGc);
-                        pw.print(" ms ago, last lowMem=");
-                        pw.print(now-proc.lastLowMemory);
-                        pw.println(" ms ago");
-                
-            }
-        }
+        needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
         
         if (mProcessCrashTimes.getMap().size() > 0) {
             if (needSep) pw.println(" ");
@@ -7283,10 +7422,17 @@
         if (dumpAll) {
             pw.println("  Total persistent processes: " + numPers);
             pw.println("  mStartRunning=" + mStartRunning
-                    + " mSystemReady=" + mSystemReady
-                    + " mBooting=" + mBooting
+                    + " mProcessesReady=" + mProcessesReady
+                    + " mSystemReady=" + mSystemReady);
+            pw.println("  mBooting=" + mBooting
                     + " mBooted=" + mBooted
                     + " mFactoryTest=" + mFactoryTest);
+            pw.print("  mLastPowerCheckRealtime=");
+                    TimeUtils.formatDuration(mLastPowerCheckRealtime, pw);
+                    pw.println("");
+            pw.print("  mLastPowerCheckUptime=");
+                    TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
+                    pw.println("");
             pw.println("  mGoingToSleep=" + mMainStack.mGoingToSleep);
             pw.println("  mLaunchingActivity=" + mMainStack.mLaunchingActivity);
             pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
@@ -7295,6 +7441,75 @@
         return true;
     }
 
+    boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean needSep, boolean dumpAll) {
+        if (mProcessesToGc.size() > 0) {
+            if (needSep) pw.println(" ");
+            needSep = true;
+            pw.println("  Processes that are waiting to GC:");
+            long now = SystemClock.uptimeMillis();
+            for (int i=0; i<mProcessesToGc.size(); i++) {
+                ProcessRecord proc = mProcessesToGc.get(i);
+                pw.print("    Process "); pw.println(proc);
+                pw.print("      lowMem="); pw.print(proc.reportLowMemory);
+                        pw.print(", last gced=");
+                        pw.print(now-proc.lastRequestedGc);
+                        pw.print(" ms ago, last lowMem=");
+                        pw.print(now-proc.lastLowMemory);
+                        pw.println(" ms ago");
+
+            }
+        }
+        return needSep;
+    }
+
+    boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll) {
+        boolean needSep = false;
+
+        if (mLruProcesses.size() > 0) {
+            ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(mLruProcesses);
+
+            Comparator<ProcessRecord> comparator = new Comparator<ProcessRecord>() {
+                @Override
+                public int compare(ProcessRecord object1, ProcessRecord object2) {
+                    if (object1.setAdj != object2.setAdj) {
+                        return object1.setAdj > object2.setAdj ? -1 : 1;
+                    }
+                    if (object1.setSchedGroup != object2.setSchedGroup) {
+                        return object1.setSchedGroup > object2.setSchedGroup ? -1 : 1;
+                    }
+                    if (object1.keeping != object2.keeping) {
+                        return object1.keeping ? -1 : 1;
+                    }
+                    if (object1.pid != object2.pid) {
+                        return object1.pid > object2.pid ? -1 : 1;
+                    }
+                    return 0;
+                }
+            };
+
+            Collections.sort(procs, comparator);
+
+            if (needSep) pw.println(" ");
+            needSep = true;
+            pw.println("  Process OOM control:");
+            dumpProcessOomList(pw, this, procs, "    ",
+                    "Proc", "PERS", true);
+            needSep = true;
+        }
+
+        needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
+
+        pw.println(" ");
+        pw.println("  mHomeProcess: " + mHomeProcess);
+        if (mHeavyWeightProcess != null) {
+            pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
+        }
+
+        return true;
+    }
+
     /**
      * There are three ways to call this:
      *  - no service specified: dump all the services
@@ -7662,84 +7877,14 @@
     
     private static final int dumpProcessList(PrintWriter pw,
             ActivityManagerService service, List list,
-            String prefix, String normalLabel, String persistentLabel,
-            boolean inclOomAdj) {
+            String prefix, String normalLabel, String persistentLabel) {
         int numPers = 0;
         final int N = list.size()-1;
         for (int i=N; i>=0; i--) {
             ProcessRecord r = (ProcessRecord)list.get(i);
-            if (false) {
-                pw.println(prefix + (r.persistent ? persistentLabel : normalLabel)
-                      + " #" + i + ":");
-                r.dump(pw, prefix + "  ");
-            } else if (inclOomAdj) {
-                String oomAdj;
-                if (r.setAdj >= EMPTY_APP_ADJ) {
-                    oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ);
-                } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) {
-                    oomAdj = buildOomTag("bak", "  ", r.setAdj, HIDDEN_APP_MIN_ADJ);
-                } else if (r.setAdj >= HOME_APP_ADJ) {
-                    oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ);
-                } else if (r.setAdj >= SECONDARY_SERVER_ADJ) {
-                    oomAdj = buildOomTag("svc", "  ", r.setAdj, SECONDARY_SERVER_ADJ);
-                } else if (r.setAdj >= BACKUP_APP_ADJ) {
-                    oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
-                } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) {
-                    oomAdj = buildOomTag("hvy  ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ);
-                } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) {
-                    oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ);
-                } else if (r.setAdj >= VISIBLE_APP_ADJ) {
-                    oomAdj = buildOomTag("vis  ", null, r.setAdj, VISIBLE_APP_ADJ);
-                } else if (r.setAdj >= FOREGROUND_APP_ADJ) {
-                    oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ);
-                } else if (r.setAdj >= CORE_SERVER_ADJ) {
-                    oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ);
-                } else if (r.setAdj >= SYSTEM_ADJ) {
-                    oomAdj = buildOomTag("sys  ", null, r.setAdj, SYSTEM_ADJ);
-                } else {
-                    oomAdj = Integer.toString(r.setAdj);
-                }
-                String schedGroup;
-                switch (r.setSchedGroup) {
-                    case Process.THREAD_GROUP_BG_NONINTERACTIVE:
-                        schedGroup = "B";
-                        break;
-                    case Process.THREAD_GROUP_DEFAULT:
-                        schedGroup = "F";
-                        break;
-                    default:
-                        schedGroup = Integer.toString(r.setSchedGroup);
-                        break;
-                }
-                pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
-                        prefix, (r.persistent ? persistentLabel : normalLabel),
-                        N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
-                if (r.adjSource != null || r.adjTarget != null) {
-                    pw.print(prefix);
-                    pw.print("          ");
-                    if (r.adjTarget instanceof ComponentName) {
-                        pw.print(((ComponentName)r.adjTarget).flattenToShortString());
-                    } else if (r.adjTarget != null) {
-                        pw.print(r.adjTarget.toString());
-                    } else {
-                        pw.print("{null}");
-                    }
-                    pw.print("<=");
-                    if (r.adjSource instanceof ProcessRecord) {
-                        pw.print("Proc{");
-                        pw.print(((ProcessRecord)r.adjSource).toShortString());
-                        pw.println("}");
-                    } else if (r.adjSource != null) {
-                        pw.println(r.adjSource.toString());
-                    } else {
-                        pw.println("{null}");
-                    }
-                }
-            } else {
-                pw.println(String.format("%s%s #%2d: %s",
-                        prefix, (r.persistent ? persistentLabel : normalLabel),
-                        i, r.toString()));
-            }
+            pw.println(String.format("%s%s #%2d: %s",
+                    prefix, (r.persistent ? persistentLabel : normalLabel),
+                    i, r.toString()));
             if (r.persistent) {
                 numPers++;
             }
@@ -7747,6 +7892,132 @@
         return numPers;
     }
 
+    private static final void dumpProcessOomList(PrintWriter pw,
+            ActivityManagerService service, List<ProcessRecord> list,
+            String prefix, String normalLabel, String persistentLabel,
+            boolean inclDetails) {
+
+        final long curRealtime = SystemClock.elapsedRealtime();
+        final long realtimeSince = curRealtime - service.mLastPowerCheckRealtime;
+        final long curUptime = SystemClock.uptimeMillis();
+        final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
+
+        final int N = list.size()-1;
+        for (int i=N; i>=0; i--) {
+            ProcessRecord r = list.get(i);
+            String oomAdj;
+            if (r.setAdj >= EMPTY_APP_ADJ) {
+                oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ);
+            } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) {
+                oomAdj = buildOomTag("bak", "  ", r.setAdj, HIDDEN_APP_MIN_ADJ);
+            } else if (r.setAdj >= HOME_APP_ADJ) {
+                oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ);
+            } else if (r.setAdj >= SECONDARY_SERVER_ADJ) {
+                oomAdj = buildOomTag("svc", "  ", r.setAdj, SECONDARY_SERVER_ADJ);
+            } else if (r.setAdj >= BACKUP_APP_ADJ) {
+                oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
+            } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) {
+                oomAdj = buildOomTag("hvy  ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ);
+            } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) {
+                oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ);
+            } else if (r.setAdj >= VISIBLE_APP_ADJ) {
+                oomAdj = buildOomTag("vis  ", null, r.setAdj, VISIBLE_APP_ADJ);
+            } else if (r.setAdj >= FOREGROUND_APP_ADJ) {
+                oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ);
+            } else if (r.setAdj >= CORE_SERVER_ADJ) {
+                oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ);
+            } else if (r.setAdj >= SYSTEM_ADJ) {
+                oomAdj = buildOomTag("sys  ", null, r.setAdj, SYSTEM_ADJ);
+            } else {
+                oomAdj = Integer.toString(r.setAdj);
+            }
+            String schedGroup;
+            switch (r.setSchedGroup) {
+                case Process.THREAD_GROUP_BG_NONINTERACTIVE:
+                    schedGroup = "B";
+                    break;
+                case Process.THREAD_GROUP_DEFAULT:
+                    schedGroup = "F";
+                    break;
+                default:
+                    schedGroup = Integer.toString(r.setSchedGroup);
+                    break;
+            }
+            pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
+                    prefix, (r.persistent ? persistentLabel : normalLabel),
+                    N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+            if (r.adjSource != null || r.adjTarget != null) {
+                pw.print(prefix);
+                pw.print("          ");
+                if (r.adjTarget instanceof ComponentName) {
+                    pw.print(((ComponentName)r.adjTarget).flattenToShortString());
+                } else if (r.adjTarget != null) {
+                    pw.print(r.adjTarget.toString());
+                } else {
+                    pw.print("{null}");
+                }
+                pw.print("<=");
+                if (r.adjSource instanceof ProcessRecord) {
+                    pw.print("Proc{");
+                    pw.print(((ProcessRecord)r.adjSource).toShortString());
+                    pw.println("}");
+                } else if (r.adjSource != null) {
+                    pw.println(r.adjSource.toString());
+                } else {
+                    pw.println("{null}");
+                }
+            }
+            if (inclDetails) {
+                pw.print(prefix);
+                pw.print("    ");
+                pw.print("oom: max="); pw.print(r.maxAdj);
+                pw.print(" hidden="); pw.print(r.hiddenAdj);
+                pw.print(" curRaw="); pw.print(r.curRawAdj);
+                pw.print(" setRaw="); pw.print(r.setRawAdj);
+                pw.print(" cur="); pw.print(r.curAdj);
+                pw.print(" set="); pw.println(r.setAdj);
+                pw.print(prefix);
+                pw.print("    ");
+                pw.print("keeping="); pw.print(r.keeping);
+                pw.print(" hidden="); pw.print(r.hidden);
+                pw.print(" empty="); pw.println(r.empty);
+
+                if (!r.keeping) {
+                    if (r.lastWakeTime != 0) {
+                        long wtime;
+                        BatteryStatsImpl stats = service.mBatteryStatsService.getActiveStatistics();
+                        synchronized (stats) {
+                            wtime = stats.getProcessWakeTime(r.info.uid,
+                                    r.pid, curRealtime);
+                        }
+                        long timeUsed = wtime - r.lastWakeTime;
+                        pw.print(prefix);
+                        pw.print("    ");
+                        pw.print("keep awake over ");
+                        TimeUtils.formatDuration(realtimeSince, pw);
+                        pw.print(" used ");
+                        TimeUtils.formatDuration(timeUsed, pw);
+                        pw.print(" (");
+                        pw.print((timeUsed*100)/realtimeSince);
+                        pw.println("%)");
+                    }
+                    if (r.lastCpuTime != 0) {
+                        long timeUsed = r.curCpuTime - r.lastCpuTime;
+                        pw.print(prefix);
+                        pw.print("    ");
+                        pw.print("run cpu over ");
+                        TimeUtils.formatDuration(uptimeSince, pw);
+                        pw.print(" used ");
+                        TimeUtils.formatDuration(timeUsed, pw);
+                        pw.print(" (");
+                        pw.print((timeUsed*100)/uptimeSince);
+                        pw.println("%)");
+                    }
+                }
+            }
+        }
+    }
+
     static final void dumpApplicationMemoryUsage(FileDescriptor fd,
             PrintWriter pw, List list, String prefix, String[] args) {
         final boolean isCheckinRequest = scanArgs(args, "--checkin");
@@ -8098,6 +8369,8 @@
                 restart = true;
             }
         }
+        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+                "Clean-up removing on hold: " + app);
         mProcessesOnHold.remove(app);
 
         if (app == mHomeProcess) {
@@ -8371,7 +8644,11 @@
         return null;
     }
 
-    private final void bumpServiceExecutingLocked(ServiceRecord r) {
+    private final void bumpServiceExecutingLocked(ServiceRecord r, String why) {
+        if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING "
+                + why + " of " + r + " in app " + r.app);
+        else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING "
+                + why + " of " + r.shortName);
         long now = SystemClock.uptimeMillis();
         if (r.executeNesting == 0 && r.app != null) {
             if (r.app.executingServices.size() == 0) {
@@ -8409,8 +8686,7 @@
                     grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
                             r.packageName, si.intent, si.getUriPermissionsLocked());
                 }
-                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r);
-                bumpServiceExecutingLocked(r);
+                bumpServiceExecutingLocked(r, "start");
                 if (!oomAdjusted) {
                     oomAdjusted = true;
                     updateOomAdjLocked(r.app);
@@ -8443,9 +8719,7 @@
         }
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
-                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r
-                        + " in " + i + ": shouldUnbind=" + i.hasBound);
-                bumpServiceExecutingLocked(r);
+                bumpServiceExecutingLocked(r, "bind");
                 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
                 if (!rebind) {
                     i.requested = true;
@@ -8480,8 +8754,7 @@
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
         app.services.add(r);
-        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent);
-        bumpServiceExecutingLocked(r);
+        bumpServiceExecutingLocked(r, "create");
         updateLruProcessLocked(app, true, true);
 
         boolean created = false;
@@ -8733,9 +9006,7 @@
                         + ": hasBound=" + ibr.hasBound);
                 if (r.app != null && r.app.thread != null && ibr.hasBound) {
                     try {
-                        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r
-                                + " for " + ibr);
-                        bumpServiceExecutingLocked(r);
+                        bumpServiceExecutingLocked(r, "bring down unbind");
                         updateOomAdjLocked(r.app);
                         ibr.hasBound = false;
                         r.app.thread.scheduleUnbindService(r,
@@ -8786,12 +9057,7 @@
             r.app.services.remove(r);
             if (r.app.thread != null) {
                 try {
-                    if (DEBUG_SERVICE) {
-                        RuntimeException here = new RuntimeException();
-                        here.fillInStackTrace();
-                        Slog.v(TAG, ">>> EXECUTING stop of " + r, here);
-                    }
-                    bumpServiceExecutingLocked(r);
+                    bumpServiceExecutingLocked(r, "stop");
                     mStoppingServices.add(r);
                     updateOomAdjLocked(r.app);
                     r.app.thread.scheduleStopService(r);
@@ -9247,9 +9513,7 @@
         if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                 && b.intent.hasBound) {
             try {
-                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s
-                        + " from " + b);
-                bumpServiceExecutingLocked(s);
+                bumpServiceExecutingLocked(s, "unbind");
                 updateOomAdjLocked(s.app);
                 b.intent.hasBound = false;
                 // Assume the client doesn't want to know about a rebind;
@@ -9470,14 +9734,20 @@
         if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
                 + ": nesting=" + r.executeNesting
                 + ", inStopping=" + inStopping + ", app=" + r.app);
+        else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
         r.executeNesting--;
         if (r.executeNesting <= 0 && r.app != null) {
+            if (DEBUG_SERVICE) Slog.v(TAG,
+                    "Nesting at 0 of " + r.shortName);
             r.app.executingServices.remove(r);
             if (r.app.executingServices.size() == 0) {
+                if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
+                        "No more executingServices of " + r.shortName);
                 mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
             }
             if (inStopping) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r);
+                if (DEBUG_SERVICE) Slog.v(TAG,
+                        "doneExecuting remove stopping " + r);
                 mStoppingServices.remove(r);
             }
             updateOomAdjLocked(r.app);
@@ -9587,20 +9857,20 @@
                 Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
                 return;
             }
+        }
 
-            long oldIdent = Binder.clearCallingIdentity();
-            try {
-                IBackupManager bm = IBackupManager.Stub.asInterface(
-                        ServiceManager.getService(Context.BACKUP_SERVICE));
-                bm.agentConnected(agentPackageName, agent);
-            } catch (RemoteException e) {
-                // can't happen; the backup manager service is local
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception trying to deliver BackupAgent binding: ");
-                e.printStackTrace();
-            } finally {
-                Binder.restoreCallingIdentity(oldIdent);
-            }
+        long oldIdent = Binder.clearCallingIdentity();
+        try {
+            IBackupManager bm = IBackupManager.Stub.asInterface(
+                    ServiceManager.getService(Context.BACKUP_SERVICE));
+            bm.agentConnected(agentPackageName, agent);
+        } catch (RemoteException e) {
+            // can't happen; the backup manager service is local
+        } catch (Exception e) {
+            Slog.w(TAG, "Exception trying to deliver BackupAgent binding: ");
+            e.printStackTrace();
+        } finally {
+            Binder.restoreCallingIdentity(oldIdent);
         }
     }
 
@@ -10118,7 +10388,7 @@
             }
             boolean replaced = false;
             if (replacePending) {
-                for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
+                for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
                     if (intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
                         if (DEBUG_BROADCAST) Slog.v(TAG,
                                 "***** DROPPING ORDERED: " + intent);
@@ -10137,35 +10407,41 @@
         return BROADCAST_SUCCESS;
     }
 
-    public final int broadcastIntent(IApplicationThread caller,
-            Intent intent, String resolvedType, IIntentReceiver resultTo,
-            int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized, boolean sticky) {
+    final Intent verifyBroadcastLocked(Intent intent) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        int flags = intent.getFlags();
+
+        if (!mProcessesReady) {
+            // if the caller really truly claims to know what they're doing, go
+            // ahead and allow the broadcast without launching any receivers
+            if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
+                intent = new Intent(intent);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+                Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
+                        + " before boot completion");
+                throw new IllegalStateException("Cannot broadcast before boot completed");
+            }
+        }
+
+        if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+            throw new IllegalArgumentException(
+                    "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+        }
+
+        return intent;
+    }
+
+    public final int broadcastIntent(IApplicationThread caller,
+            Intent intent, String resolvedType, IIntentReceiver resultTo,
+            int resultCode, String resultData, Bundle map,
+            String requiredPermission, boolean serialized, boolean sticky) {
         synchronized(this) {
-            int flags = intent.getFlags();
-            
-            if (!mSystemReady) {
-                // if the caller really truly claims to know what they're doing, go
-                // ahead and allow the broadcast without launching any receivers
-                if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
-                    intent = new Intent(intent);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0){
-                    Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
-                            + " before boot completion");
-                    throw new IllegalStateException("Cannot broadcast before boot completed");
-                }
-            }
-            
-            if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
-                throw new IllegalArgumentException(
-                        "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
-            }
+            intent = verifyBroadcastLocked(intent);
             
             final ProcessRecord callerApp = getRecordForAppLocked(caller);
             final int callingPid = Binder.getCallingPid();
@@ -10186,6 +10462,8 @@
             int resultCode, String resultData, Bundle map,
             String requiredPermission, boolean serialized, boolean sticky) {
         synchronized(this) {
+            intent = verifyBroadcastLocked(intent);
+
             final long origId = Binder.clearCallingIdentity();
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
                     resultTo, resultCode, resultData, map, requiredPermission,
@@ -10305,7 +10583,7 @@
         Binder.restoreCallingIdentity(origId);
     }
 
-    private final void logBroadcastReceiverDiscard(BroadcastRecord r) {
+    private final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
         if (r.nextReceiver > 0) {
             Object curReceiver = r.receivers.get(r.nextReceiver-1);
             if (curReceiver instanceof BroadcastFilter) {
@@ -10333,72 +10611,115 @@
         }
     }
 
-    private final void broadcastTimeout() {
+    private final void setBroadcastTimeoutLocked(long timeoutTime) {
+        if (! mPendingBroadcastTimeoutMessage) {
+            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
+            mHandler.sendMessageAtTime(msg, timeoutTime);
+            mPendingBroadcastTimeoutMessage = true;
+        }
+    }
+
+    private final void cancelBroadcastTimeoutLocked() {
+        if (mPendingBroadcastTimeoutMessage) {
+            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
+            mPendingBroadcastTimeoutMessage = false;
+        }
+    }
+
+    private final void broadcastTimeoutLocked(boolean fromMsg) {
+        if (fromMsg) {
+            mPendingBroadcastTimeoutMessage = false;
+        }
+
+        if (mOrderedBroadcasts.size() == 0) {
+            return;
+        }
+
+        long now = SystemClock.uptimeMillis();
+        BroadcastRecord r = mOrderedBroadcasts.get(0);
+        if (fromMsg) {
+            if (mDidDexOpt) {
+                // Delay timeouts until dexopt finishes.
+                mDidDexOpt = false;
+                long timeoutTime = SystemClock.uptimeMillis() + BROADCAST_TIMEOUT;
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+            if (! mProcessesReady) {
+                // Only process broadcast timeouts if the system is ready. That way
+                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
+                // to do heavy lifting for system up.
+                return;
+            }
+
+            long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
+            if (timeoutTime > now) {
+                // We can observe premature timeouts because we do not cancel and reset the
+                // broadcast timeout message after each receiver finishes.  Instead, we set up
+                // an initial timeout then kick it down the road a little further as needed
+                // when it expires.
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+                        + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+        }
+
+        Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+                + ", started " + (now - r.receiverTime) + "ms ago");
+        r.receiverTime = now;
+        r.anrCount++;
+
+        // Current receiver has passed its expiration date.
+        if (r.nextReceiver <= 0) {
+            Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
+            return;
+        }
+
         ProcessRecord app = null;
         String anrMessage = null;
 
-        synchronized (this) {
-            if (mOrderedBroadcasts.size() == 0) {
-                return;
-            }
-            long now = SystemClock.uptimeMillis();
-            BroadcastRecord r = mOrderedBroadcasts.get(0);
-            if ((r.receiverTime+BROADCAST_TIMEOUT) > now) {
-                if (DEBUG_BROADCAST) Slog.v(TAG,
-                        "Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
-                        + (r.receiverTime + BROADCAST_TIMEOUT));
-                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
-                mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT);
-                return;
-            }
-
-            Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver);
-            r.receiverTime = now;
-            r.anrCount++;
-
-            // Current receiver has passed its expiration date.
-            if (r.nextReceiver <= 0) {
-                Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
-                return;
-            }
-
-            Object curReceiver = r.receivers.get(r.nextReceiver-1);
-            Slog.w(TAG, "Receiver during timeout: " + curReceiver);
-            logBroadcastReceiverDiscard(r);
-            if (curReceiver instanceof BroadcastFilter) {
-                BroadcastFilter bf = (BroadcastFilter)curReceiver;
-                if (bf.receiverList.pid != 0
-                        && bf.receiverList.pid != MY_PID) {
-                    synchronized (this.mPidsSelfLocked) {
-                        app = this.mPidsSelfLocked.get(
-                                bf.receiverList.pid);
-                    }
+        Object curReceiver = r.receivers.get(r.nextReceiver-1);
+        Slog.w(TAG, "Receiver during timeout: " + curReceiver);
+        logBroadcastReceiverDiscardLocked(r);
+        if (curReceiver instanceof BroadcastFilter) {
+            BroadcastFilter bf = (BroadcastFilter)curReceiver;
+            if (bf.receiverList.pid != 0
+                    && bf.receiverList.pid != MY_PID) {
+                synchronized (this.mPidsSelfLocked) {
+                    app = this.mPidsSelfLocked.get(
+                            bf.receiverList.pid);
                 }
-            } else {
-                app = r.curApp;
             }
-            
-            if (app != null) {
-                anrMessage = "Broadcast of " + r.intent.toString();
-            }
-
-            if (mPendingBroadcast == r) {
-                mPendingBroadcast = null;
-            }
-
-            // Move on to the next receiver.
-            finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
-                    r.resultExtras, r.resultAbort, true);
-            scheduleBroadcastsLocked();
+        } else {
+            app = r.curApp;
         }
         
+        if (app != null) {
+            anrMessage = "Broadcast of " + r.intent.toString();
+        }
+
+        if (mPendingBroadcast == r) {
+            mPendingBroadcast = null;
+        }
+
+        // Move on to the next receiver.
+        finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
+                r.resultExtras, r.resultAbort, true);
+        scheduleBroadcastsLocked();
+
         if (anrMessage != null) {
-            appNotResponding(app, null, null, anrMessage);
+            // Post the ANR to the handler since we do not want to process ANRs while
+            // potentially holding our lock.
+            mHandler.post(new AppNotResponding(app, anrMessage));
         }
     }
 
     private final void processCurBroadcastLocked(BroadcastRecord r,
             ProcessRecord app) throws RemoteException {
+        if (DEBUG_BROADCAST)  Slog.v(TAG,
+                "Process cur broadcast " + r + " for app " + app);
         if (app.thread == null) {
             throw new RemoteException();
         }
@@ -10418,9 +10739,13 @@
             ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                     r.resultCode, r.resultData, r.resultExtras, r.ordered);
+            if (DEBUG_BROADCAST)  Slog.v(TAG,
+                    "Process cur broadcast " + r + " DELIVERED for app " + app);
             started = true;
         } finally {
             if (!started) {
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Process cur broadcast " + r + ": NOT STARTED!");
                 r.receiver = null;
                 r.curApp = null;
                 app.curReceiver = null;
@@ -10429,9 +10754,10 @@
 
     }
 
-    static void performReceive(ProcessRecord app, IIntentReceiver receiver,
+    static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
             Intent intent, int resultCode, String data, Bundle extras,
             boolean ordered, boolean sticky) throws RemoteException {
+        // Send the intent to the receiver asynchronously using one-way binder calls.
         if (app != null && app.thread != null) {
             // If we have an app thread, do the call through that so it is
             // correctly ordered with other one-way calls.
@@ -10442,7 +10768,7 @@
         }
     }
     
-    private final void deliverToRegisteredReceiver(BroadcastRecord r,
+    private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
             BroadcastFilter filter, boolean ordered) {
         boolean skip = false;
         if (filter.requiredPermission != null) {
@@ -10500,7 +10826,7 @@
                     Slog.i(TAG, "Delivering to " + filter
                             + " (seq=" + seq + "): " + r);
                 }
-                performReceive(filter.receiverList.app, filter.receiverList.receiver,
+                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                     new Intent(r.intent), r.resultCode,
                     r.resultData, r.resultExtras, r.ordered, r.initialSticky);
                 if (ordered) {
@@ -10557,7 +10883,7 @@
                     if (DEBUG_BROADCAST)  Slog.v(TAG,
                             "Delivering non-ordered to registered "
                             + target + ": " + r);
-                    deliverToRegisteredReceiver(r, (BroadcastFilter)target, false);
+                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                 }
                 addBroadcastToHistoryLocked(r);
                 if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast "
@@ -10585,6 +10911,8 @@
                 } else {
                     Slog.w(TAG, "pending app " + mPendingBroadcast.curApp
                             + " died before responding to broadcast");
+                    mPendingBroadcast.state = BroadcastRecord.IDLE;
+                    mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
                     mPendingBroadcast = null;
                 }
             }
@@ -10611,11 +10939,11 @@
                 // and continue to make progress.
                 //
                 // This is only done if the system is ready so that PRE_BOOT_COMPLETED
-                // receivers don't get executed with with timeouts. They're intended for
+                // receivers don't get executed with timeouts. They're intended for
                 // one time heavy lifting after system upgrades and can take
                 // significant amounts of time.
                 int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
-                if (mSystemReady && r.dispatchTime > 0) {
+                if (mProcessesReady && r.dispatchTime > 0) {
                     long now = SystemClock.uptimeMillis();
                     if ((numReceivers > 0) &&
                             (now > r.dispatchTime + (2*BROADCAST_TIMEOUT*numReceivers))) {
@@ -10627,7 +10955,7 @@
                                 + " numReceivers=" + numReceivers
                                 + " nextReceiver=" + r.nextReceiver
                                 + " state=" + r.state);
-                        broadcastTimeout(); // forcibly finish this broadcast
+                        broadcastTimeoutLocked(false); // forcibly finish this broadcast
                         forceReceive = true;
                         r.state = BroadcastRecord.IDLE;
                     }
@@ -10651,7 +10979,7 @@
                                 Slog.i(TAG, "Finishing broadcast " + r.intent.getAction()
                                         + " seq=" + seq + " app=" + r.callerApp);
                             }
-                            performReceive(r.callerApp, r.resultTo,
+                            performReceiveLocked(r.callerApp, r.resultTo,
                                 new Intent(r.intent), r.resultCode,
                                 r.resultData, r.resultExtras, false, false);
                         } catch (RemoteException e) {
@@ -10660,7 +10988,7 @@
                     }
                     
                     if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
-                    mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
+                    cancelBroadcastTimeoutLocked();
 
                     if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
                             + r);
@@ -10685,11 +11013,12 @@
 
                 if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast "
                         + r);
+            }
+            if (! mPendingBroadcastTimeoutMessage) {
+                long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
                 if (DEBUG_BROADCAST) Slog.v(TAG,
-                        "Submitting BROADCAST_TIMEOUT_MSG for "
-                        + (r.receiverTime + BROADCAST_TIMEOUT));
-                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
-                mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT);
+                        "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
             }
 
             Object nextReceiver = r.receivers.get(recIdx);
@@ -10700,7 +11029,7 @@
                 if (DEBUG_BROADCAST)  Slog.v(TAG,
                         "Delivering ordered to registered "
                         + filter + ": " + r);
-                deliverToRegisteredReceiver(r, filter, r.ordered);
+                deliverToRegisteredReceiverLocked(r, filter, r.ordered);
                 if (r.receiver == null || !r.ordered) {
                     // The receiver has already finished, so schedule to
                     // process the next one.
@@ -10754,10 +11083,15 @@
             }
             if (r.curApp != null && r.curApp.crashing) {
                 // If the target process is crashing, just skip it.
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Skipping deliver ordered " + r + " to " + r.curApp
+                        + ": process crashing");
                 skip = true;
             }
 
             if (skip) {
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Skipping delivery of ordered " + r + " for whatever reason");
                 r.receiver = null;
                 r.curFilter = null;
                 r.state = BroadcastRecord.IDLE;
@@ -10789,6 +11123,8 @@
             }
 
             // Not running -- get it started, to be executed when the app comes up.
+            if (DEBUG_BROADCAST)  Slog.v(TAG,
+                    "Need to start app " + targetProcess + " for broadcast " + r);
             if ((r.curApp=startProcessLocked(targetProcess,
                     info.activityInfo.applicationInfo, true,
                     r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
@@ -10801,7 +11137,7 @@
                         + info.activityInfo.applicationInfo.packageName + "/"
                         + info.activityInfo.applicationInfo.uid + " for broadcast "
                         + r.intent + ": process is bad");
-                logBroadcastReceiverDiscard(r);
+                logBroadcastReceiverDiscardLocked(r);
                 finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
                         r.resultExtras, r.resultAbort, true);
                 scheduleBroadcastsLocked();
@@ -10810,6 +11146,7 @@
             }
 
             mPendingBroadcast = r;
+            mPendingBroadcastRecvIndex = recIdx;
         }
     }
 
@@ -11129,6 +11466,7 @@
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.maxAdj;
+            app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
             return (app.curAdj=app.maxAdj);
        }
@@ -11136,6 +11474,7 @@
         app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
         app.adjSource = null;
         app.adjTarget = null;
+        app.keeping = false;
         app.empty = false;
         app.hidden = false;
 
@@ -11265,6 +11604,9 @@
                     if (adj > SECONDARY_SERVER_ADJ) {
                         app.adjType = "started-bg-services";
                     }
+                    // Don't kill this process because it is doing work; it
+                    // has said it is doing work.
+                    app.keeping = true;
                 }
                 if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ
                         || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
@@ -11298,6 +11640,9 @@
                                     if (!client.hidden) {
                                         app.hidden = false;
                                     }
+                                    if (client.keeping) {
+                                        app.keeping = true;
+                                    }
                                     app.adjType = "service";
                                     app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                             .REASON_SERVICE_IN_USE;
@@ -11331,7 +11676,7 @@
                 }
             }
             
-            // Finally, f this process has active services running in it, we
+            // Finally, if this process has active services running in it, we
             // would like to avoid killing it unless it would prevent the current
             // application from running.  By default we put the process in
             // with the rest of the background processes; as we scan through
@@ -11373,6 +11718,9 @@
                             if (!client.hidden) {
                                 app.hidden = false;
                             }
+                            if (client.keeping) {
+                                app.keeping = true;
+                            }
                             app.adjType = "provider";
                             app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                     .REASON_PROVIDER_IN_USE;
@@ -11392,6 +11740,7 @@
                         adj = FOREGROUND_APP_ADJ;
                         schedGroup = Process.THREAD_GROUP_DEFAULT;
                         app.hidden = false;
+                        app.keeping = true;
                         app.adjType = "provider";
                         app.adjTarget = cpr.name;
                     }
@@ -11409,6 +11758,9 @@
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             }
         }
+        if (adj < HIDDEN_APP_MIN_ADJ) {
+            app.keeping = true;
+        }
 
         app.curAdj = adj;
         app.curSchedGroup = schedGroup;
@@ -11546,57 +11898,99 @@
         }
     }
 
-    final void checkExcessiveWakeLocksLocked(boolean doKills) {
+    final void checkExcessivePowerUsageLocked(boolean doKills) {
+        updateCpuStatsNow();
+
         BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
-        if (mLastWakeLockCheckTime == 0) {
-            doKills = false;
+        boolean doWakeKills = doKills;
+        boolean doCpuKills = doKills;
+        if (mLastPowerCheckRealtime == 0) {
+            doWakeKills = false;
+        }
+        if (mLastPowerCheckUptime == 0) {
+            doCpuKills = false;
         }
         if (stats.isScreenOn()) {
-            doKills = false;
+            doWakeKills = false;
         }
         final long curRealtime = SystemClock.elapsedRealtime();
-        final long timeSince = curRealtime - mLastWakeLockCheckTime;
-        mLastWakeLockCheckTime = curRealtime;
-        if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) {
-            doKills = false;
+        final long realtimeSince = curRealtime - mLastPowerCheckRealtime;
+        final long curUptime = SystemClock.uptimeMillis();
+        final long uptimeSince = curUptime - mLastPowerCheckUptime;
+        mLastPowerCheckRealtime = curRealtime;
+        mLastPowerCheckUptime = curUptime;
+        if (realtimeSince < WAKE_LOCK_MIN_CHECK_DURATION) {
+            doWakeKills = false;
+        }
+        if (uptimeSince < CPU_MIN_CHECK_DURATION) {
+            doCpuKills = false;
         }
         int i = mLruProcesses.size();
         while (i > 0) {
             i--;
             ProcessRecord app = mLruProcesses.get(i);
-            if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {
+            if (!app.keeping) {
                 long wtime;
                 synchronized (stats) {
                     wtime = stats.getProcessWakeTime(app.info.uid,
                             app.pid, curRealtime);
                 }
-                long timeUsed = wtime - app.lastWakeTime;
-                if (false) {
+                long wtimeUsed = wtime - app.lastWakeTime;
+                long cputimeUsed = app.curCpuTime - app.lastCpuTime;
+                if (DEBUG_POWER) {
                     StringBuilder sb = new StringBuilder(128);
                     sb.append("Wake for ");
                     app.toShortString(sb);
                     sb.append(": over ");
-                    TimeUtils.formatDuration(timeSince, sb);
+                    TimeUtils.formatDuration(realtimeSince, sb);
                     sb.append(" used ");
-                    TimeUtils.formatDuration(timeUsed, sb);
+                    TimeUtils.formatDuration(wtimeUsed, sb);
                     sb.append(" (");
-                    sb.append((timeUsed*100)/timeSince);
+                    sb.append((wtimeUsed*100)/realtimeSince);
+                    sb.append("%)");
+                    Slog.i(TAG, sb.toString());
+                    sb.setLength(0);
+                    sb.append("CPU for ");
+                    app.toShortString(sb);
+                    sb.append(": over ");
+                    TimeUtils.formatDuration(uptimeSince, sb);
+                    sb.append(" used ");
+                    TimeUtils.formatDuration(cputimeUsed, sb);
+                    sb.append(" (");
+                    sb.append((cputimeUsed*100)/uptimeSince);
                     sb.append("%)");
                     Slog.i(TAG, sb.toString());
                 }
                 // If a process has held a wake lock for more
                 // than 50% of the time during this period,
                 // that sounds pad.  Kill!
-                if (doKills && timeSince > 0
-                        && ((timeUsed*100)/timeSince) >= 50) {
-                    Slog.i(TAG, "Excessive wake lock in " + app.processName
-                            + " (pid " + app.pid + "): held " + timeUsed
-                            + " during " + timeSince);
+                if (doWakeKills && realtimeSince > 0
+                        && ((wtimeUsed*100)/realtimeSince) >= 50) {
+                    synchronized (stats) {
+                        stats.reportExcessiveWakeLocked(app.info.uid, app.processName,
+                                realtimeSince, wtimeUsed);
+                    }
+                    Slog.w(TAG, "Excessive wake lock in " + app.processName
+                            + " (pid " + app.pid + "): held " + wtimeUsed
+                            + " during " + realtimeSince);
                     EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
                             app.processName, app.setAdj, "excessive wake lock");
                     Process.killProcessQuiet(app.pid);
+                } else if (doCpuKills && uptimeSince > 0
+                        && ((cputimeUsed*100)/uptimeSince) >= 50) {
+                    synchronized (stats) {
+                        stats.reportExcessiveCpuLocked(app.info.uid, app.processName,
+                                uptimeSince, cputimeUsed);
+                    }
+                    Slog.w(TAG, "Excessive CPU in " + app.processName
+                            + " (pid " + app.pid + "): used " + cputimeUsed
+                            + " during " + uptimeSince);
+                    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                            app.processName, app.setAdj, "excessive cpu");
+                    Process.killProcessQuiet(app.pid);
                 } else {
                     app.lastWakeTime = wtime;
+                    app.lastCpuTime = app.curCpuTime;
                 }
             }
         }
@@ -11610,6 +12004,8 @@
             return true;
         }
 
+        final boolean wasKeeping = app.keeping;
+
         int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
 
         if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) {
@@ -11624,13 +12020,20 @@
                     // Likewise do a gc when an app is moving in to the
                     // background (such as a service stopping).
                     scheduleAppGcLocked(app);
-                    // And note its current wake lock time.
+                }
+
+                if (wasKeeping && !app.keeping) {
+                    // This app is no longer something we want to keep.  Note
+                    // its current wake lock time to later know to kill it if
+                    // it is not behaving well.
                     BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
                     synchronized (stats) {
                         app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
                                 app.pid, SystemClock.elapsedRealtime());
                     }
+                    app.lastCpuTime = app.curCpuTime;
                 }
+
                 app.setRawAdj = app.curRawAdj;
             }
             if (adj != app.setAdj) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 4fc8020..a4497d6 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1104,26 +1104,30 @@
         // Okay we are now going to start a switch, to 'next'.  We may first
         // have to pause the current activity, but this is an important point
         // where we have decided to go to 'next' so keep track of that.
-        if (mLastStartedActivity != null && !mLastStartedActivity.finishing) {
-            long now = SystemClock.uptimeMillis();
-            final boolean inTime = mLastStartedActivity.startTime != 0
-                    && (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
-            final int lastUid = mLastStartedActivity.info.applicationInfo.uid;
-            final int nextUid = next.info.applicationInfo.uid;
-            if (inTime && lastUid != nextUid
-                    && lastUid != next.launchedFromUid
-                    && mService.checkPermission(
-                            android.Manifest.permission.STOP_APP_SWITCHES,
-                            -1, next.launchedFromUid)
-                    != PackageManager.PERMISSION_GRANTED) {
-                mService.showLaunchWarningLocked(mLastStartedActivity, next);
+        // XXX "App Redirected" dialog is getting too many false positives
+        // at this point, so turn off for now.
+        if (false) {
+            if (mLastStartedActivity != null && !mLastStartedActivity.finishing) {
+                long now = SystemClock.uptimeMillis();
+                final boolean inTime = mLastStartedActivity.startTime != 0
+                        && (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
+                final int lastUid = mLastStartedActivity.info.applicationInfo.uid;
+                final int nextUid = next.info.applicationInfo.uid;
+                if (inTime && lastUid != nextUid
+                        && lastUid != next.launchedFromUid
+                        && mService.checkPermission(
+                                android.Manifest.permission.STOP_APP_SWITCHES,
+                                -1, next.launchedFromUid)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    mService.showLaunchWarningLocked(mLastStartedActivity, next);
+                } else {
+                    next.startTime = now;
+                    mLastStartedActivity = next;
+                }
             } else {
-                next.startTime = now;
+                next.startTime = SystemClock.uptimeMillis();
                 mLastStartedActivity = next;
             }
-        } else {
-            next.startTime = SystemClock.uptimeMillis();
-            mLastStartedActivity = next;
         }
         
         // We need to start pausing the current activity so the top one
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 67df707..404c6be 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -60,6 +60,7 @@
     int setAdj;                 // Last set OOM adjustment for this process
     int curSchedGroup;          // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
+    boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean foregroundServices; // Running any services that are foreground?
     boolean bad;                // True if disabled in the bad process list
@@ -75,6 +76,8 @@
     ComponentName instrumentationResultClass;// copy of instrumentationClass
     BroadcastRecord curReceiver;// receiver currently running in the app
     long lastWakeTime;          // How long proc held wake lock at last check
+    long lastCpuTime;           // How long proc has run CPU at last check
+    long curCpuTime;            // How long proc has run CPU most recently
     long lastRequestedGc;       // When we last asked the app to do a gc
     long lastLowMemory;         // When we last told the app that memory is low
     boolean reportLowMemory;    // Set to true when waiting to report low mem
@@ -131,13 +134,6 @@
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
 
-        long wtime;
-        synchronized (batteryStats.getBatteryStats()) {
-            wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid,
-                    pid, SystemClock.elapsedRealtime());
-        }
-        long timeUsed = wtime - lastWakeTime;
-
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -170,6 +166,7 @@
         pw.print(prefix); pw.print("lastActivityTime=");
                 TimeUtils.formatDuration(lastActivityTime, now, pw);
                 pw.print(" lruWeight="); pw.print(lruWeight);
+                pw.print(" keeping="); pw.print(keeping);
                 pw.print(" hidden="); pw.print(hidden);
                 pw.print(" empty="); pw.println(empty);
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -188,9 +185,20 @@
                 pw.print(" persistentActivities="); pw.println(persistentActivities);
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.println(lruSeq);
-        pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
-                pw.print(" time used=");
-                TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+        if (!keeping) {
+            long wtime;
+            synchronized (batteryStats.getBatteryStats()) {
+                wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid,
+                        pid, SystemClock.elapsedRealtime());
+            }
+            long timeUsed = wtime - lastWakeTime;
+            pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
+                    pw.print(" time used=");
+                    TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+            pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
+                    pw.print(" time used=");
+                    TimeUtils.formatDuration(curCpuTime-lastCpuTime, pw); pw.println("");
+        }
         pw.print(prefix); pw.print("lastRequestedGc=");
                 TimeUtils.formatDuration(lastRequestedGc, now, pw);
                 pw.print(" lastLowMemory=");
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index f35a68e..e5aceb4 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -347,7 +347,9 @@
                         // If it gave us a garbage notification, it doesn't
                         // get to be foreground.
                         ams.setServiceForeground(name, ServiceRecord.this,
-                                localForegroundId, null, true);
+                                0, null, true);
+                        ams.crashApplication(appUid, appPid, localPackageName,
+                                "Bad notification for startForeground: " + e);
                     }
                 }
             });
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 0cb6943..e3347cb 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -27,8 +27,8 @@
  *
  * CTS tests for this functionality can be run with "runtest cts-appsecurity".
  *
- * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert
- *      /src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ *      src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
  */
 class UriPermission {
     final int uid;
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 3bf6ee4..39ce0b6 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -1171,7 +1171,10 @@
     private void reportAGpsStatus(int type, int status) {
         switch (status) {
             case GPS_REQUEST_AGPS_DATA_CONN:
-                 int result = mConnMgr.startUsingNetworkFeature(
+                // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
+                //  to avoid a race condition with handleUpdateNetworkState()
+                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+                int result = mConnMgr.startUsingNetworkFeature(
                         ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
                 if (result == Phone.APN_ALREADY_ACTIVE) {
                     if (mAGpsApn != null) {
@@ -1179,11 +1182,13 @@
                         mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
                     } else {
                         Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE");
+                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
                         native_agps_data_conn_failed();
                     }
                 } else if (result == Phone.APN_REQUEST_STARTED) {
-                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+                    // Nothing to do here
                 } else {
+                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
                     native_agps_data_conn_failed();
                 }
                 break;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index d27c2c6..6f52f24 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -138,6 +138,14 @@
     jfieldID mMotionRanges;
 } gInputDeviceClassInfo;
 
+static struct {
+    jclass clazz;
+
+    jfieldID touchscreen;
+    jfieldID keyboard;
+    jfieldID navigation;
+} gConfigurationClassInfo;
+
 // ----------------------------------------------------------------------------
 
 static inline nsecs_t now() {
@@ -199,7 +207,7 @@
     virtual int32_t getMaxEventsPerSecond();
     virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
             const KeyEvent* keyEvent, uint32_t policyFlags);
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType);
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
     virtual bool checkInjectEventsPermissionNonReentrant(
             int32_t injectorPid, int32_t injectorUid);
 
@@ -698,11 +706,7 @@
 
     JNIEnv* env = jniEnv();
 
-    InputConfiguration config;
-    mInputManager->getReader()->getInputConfiguration(& config);
-
-    env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
-            when, config.touchScreen, config.keyboard, config.navigation);
+    env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged, when);
     checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
 }
 
@@ -969,10 +973,8 @@
     return consumed && ! error;
 }
 
-void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) {
-    if (windowType != InputWindow::TYPE_KEYGUARD) {
-        android_server_PowerManagerService_userActivity(eventTime, eventType);
-    }
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+    android_server_PowerManagerService_userActivity(eventTime, eventType);
 }
 
 
@@ -1270,6 +1272,20 @@
     return deviceIdsObj;
 }
 
+static void android_server_InputManager_nativeGetInputConfiguration(JNIEnv* env,
+        jclass clazz, jobject configObj) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    InputConfiguration config;
+    gNativeInputManager->getInputManager()->getReader()->getInputConfiguration(& config);
+
+    env->SetIntField(configObj, gConfigurationClassInfo.touchscreen, config.touchScreen);
+    env->SetIntField(configObj, gConfigurationClassInfo.keyboard, config.keyboard);
+    env->SetIntField(configObj, gConfigurationClassInfo.navigation, config.navigation);
+}
+
 static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
     if (checkInputManagerUnitialized(env)) {
         return NULL;
@@ -1316,6 +1332,8 @@
             (void*) android_server_InputManager_nativeGetInputDevice },
     { "nativeGetInputDeviceIds", "()[I",
             (void*) android_server_InputManager_nativeGetInputDeviceIds },
+    { "nativeGetInputConfiguration", "(Landroid/content/res/Configuration;)V",
+            (void*) android_server_InputManager_nativeGetInputConfiguration },
     { "nativeDump", "()Ljava/lang/String;",
             (void*) android_server_InputManager_nativeDump },
 };
@@ -1343,7 +1361,7 @@
     FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
 
     GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz,
-            "notifyConfigurationChanged", "(JIII)V");
+            "notifyConfigurationChanged", "(J)V");
 
     GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz,
             "notifyLidSwitchChanged", "(JZ)V");
@@ -1543,6 +1561,19 @@
     GET_FIELD_ID(gInputDeviceClassInfo.mMotionRanges, gInputDeviceClassInfo.clazz,
             "mMotionRanges", "[Landroid/view/InputDevice$MotionRange;");
 
+    // Configuration
+
+    FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");
+
+    GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz,
+            "touchscreen", "I");
+
+    GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz,
+            "keyboard", "I");
+
+    GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
+            "navigation", "I");
+
     return 0;
 }
 
diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
index 850866a..493122d 100644
--- a/services/surfaceflinger/GLExtensions.cpp
+++ b/services/surfaceflinger/GLExtensions.cpp
@@ -92,6 +92,10 @@
         // hack for Adreno 200
         mHaveTextureExternal = true;
     }
+
+    if (hasExtension("GL_OES_framebuffer_object")) {
+        mHaveFramebufferObject = true;
+    }
 }
 
 bool GLExtensions::hasExtension(char const* extension) const
diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h
index bbb284e..c86c66a 100644
--- a/services/surfaceflinger/GLExtensions.h
+++ b/services/surfaceflinger/GLExtensions.h
@@ -39,6 +39,7 @@
     bool mHaveTextureExternal   : 1;
     bool mHaveNpot              : 1;
     bool mHaveDirectTexture     : 1;
+    bool mHaveFramebufferObject : 1;
 
     String8 mVendor;
     String8 mRenderer;
@@ -66,6 +67,10 @@
         return mHaveDirectTexture;
     }
 
+    inline bool haveFramebufferObject() const {
+        return mHaveFramebufferObject;
+    }
+
     void initWithGLStrings(
             GLubyte const* vendor,
             GLubyte const* renderer,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc241d4e..2b06f6f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -75,6 +75,7 @@
         mBootTime(systemTime()),
         mHardwareTest("android.permission.HARDWARE_TEST"),
         mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
+        mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
         mDeferReleaseConsole(false),
@@ -1465,8 +1466,23 @@
                         "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
                 return PERMISSION_DENIED;
             }
+            break;
+        }
+        case CAPTURE_SCREEN:
+        {
+            // codes that require permission check
+            IPCThreadState* ipc = IPCThreadState::self();
+            const int pid = ipc->getCallingPid();
+            const int uid = ipc->getCallingUid();
+            if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
+                LOGE("Permission Denial: "
+                        "can't read framebuffer pid=%d, uid=%d", pid, uid);
+                return PERMISSION_DENIED;
+            }
+            break;
         }
     }
+
     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
     if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
         CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1530,6 +1546,139 @@
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::captureScreen(DisplayID dpy,
+        sp<IMemoryHeap>* heap,
+        uint32_t* width, uint32_t* height, PixelFormat* format)
+{
+    // only one display supported for now
+    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+        return BAD_VALUE;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+    class MessageCaptureScreen : public MessageBase {
+        SurfaceFlinger* flinger;
+        DisplayID dpy;
+        sp<IMemoryHeap>* heap;
+        uint32_t* w;
+        uint32_t* h;
+        PixelFormat* f;
+        status_t result;
+    public:
+        MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
+                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+            : flinger(flinger), dpy(dpy),
+              heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+        {
+        }
+        status_t getResult() const {
+            return result;
+        }
+        virtual bool handler() {
+            Mutex::Autolock _l(flinger->mStateLock);
+
+            // if we have secure windows, never allow the screen capture
+            if (flinger->mSecureFrameBuffer)
+                return true;
+
+            // make sure to clear all GL error flags
+            while ( glGetError() != GL_NO_ERROR ) ;
+
+            // get screen geometry
+            const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
+            const uint32_t sw = hw.getWidth();
+            const uint32_t sh = hw.getHeight();
+            const Region screenBounds(hw.bounds());
+            const size_t size = sw * sh * 4;
+
+            // create a FBO
+            GLuint name, tname;
+            glGenRenderbuffersOES(1, &tname);
+            glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+            glGenFramebuffersOES(1, &name);
+            glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+                    GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+            GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+            if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+                // invert everything, b/c glReadPixel() below will invert the FB
+                glMatrixMode(GL_PROJECTION);
+                glPushMatrix();
+                glLoadIdentity();
+                glOrthof(0, sw, 0, sh, 0, 1);
+                glMatrixMode(GL_MODELVIEW);
+
+                // redraw the screen entirely...
+                glClearColor(0,0,0,1);
+                glClear(GL_COLOR_BUFFER_BIT);
+                const Vector< sp<LayerBase> >& layers(
+                        flinger->mVisibleLayersSortedByZ);
+                const size_t count = layers.size();
+                for (size_t i=0 ; i<count ; ++i) {
+                    const sp<LayerBase>& layer(layers[i]);
+                    if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
+                        // we cannot render LayerBuffer because it doens't
+                        // use OpenGL, and won't show-up in the FBO.
+                        continue;
+                    }
+                    layer->draw(screenBounds);
+                }
+
+                glMatrixMode(GL_PROJECTION);
+                glPopMatrix();
+                glMatrixMode(GL_MODELVIEW);
+
+                // check for errors and return screen capture
+                if (glGetError() != GL_NO_ERROR) {
+                    // error while rendering
+                    result = INVALID_OPERATION;
+                } else {
+                    // allocate shared memory large enough to hold the
+                    // screen capture
+                    sp<MemoryHeapBase> base(
+                            new MemoryHeapBase(size, 0, "screen-capture") );
+                    void* const ptr = base->getBase();
+                    if (ptr) {
+                        // capture the screen with glReadPixels()
+                        glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+                        if (glGetError() == GL_NO_ERROR) {
+                            *heap = base;
+                            *w = sw;
+                            *h = sh;
+                            *f = PIXEL_FORMAT_RGBA_8888;
+                            result = NO_ERROR;
+                        }
+                    } else {
+                        result = NO_MEMORY;
+                    }
+                }
+            } else {
+                result = BAD_VALUE;
+            }
+
+            // release FBO resources
+            glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+            glDeleteRenderbuffersOES(1, &tname);
+            glDeleteFramebuffersOES(1, &name);
+            return true;
+        }
+    };
+
+    sp<MessageBase> msg = new MessageCaptureScreen(this,
+            dpy, heap, width, height, format);
+    status_t res = postMessageSync(msg);
+    if (res == NO_ERROR) {
+        res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
+    }
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+
 sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
 {
     sp<Layer> result;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8ecfc01..f09fdbc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -193,6 +193,11 @@
     virtual status_t                    unfreezeDisplay(DisplayID dpy, uint32_t flags);
     virtual int                         setOrientation(DisplayID dpy, int orientation, uint32_t flags);
     virtual void                        signal() const;
+    virtual status_t                    captureScreen(DisplayID dpy,
+                                                      sp<IMemoryHeap>* heap,
+                                                      uint32_t* width,
+                                                      uint32_t* height,
+                                                      PixelFormat* format);
 
             void                        screenReleased(DisplayID dpy);
             void                        screenAcquired(DisplayID dpy);
@@ -361,6 +366,7 @@
                 nsecs_t                     mBootTime;
                 Permission                  mHardwareTest;
                 Permission                  mAccessSurfaceFlinger;
+                Permission                  mReadFramebuffer;
                 Permission                  mDump;
                 
                 // Can only accessed from the main thread, these members
diff --git a/services/surfaceflinger/tests/screencap/Android.mk b/services/surfaceflinger/tests/screencap/Android.mk
new file mode 100644
index 0000000..1cfb471
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/Android.mk
@@ -0,0 +1,26 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	screencap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	libbinder \
+	libskia \
+    libui \
+    libsurfaceflinger_client
+
+LOCAL_MODULE:= test-screencap
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_C_INCLUDES += \
+	external/skia/include/core \
+	external/skia/include/effects \
+	external/skia/include/images \
+	external/skia/src/ports \
+	external/skia/include/utils
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
new file mode 100644
index 0000000..9e893f4
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -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.
+ */
+
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <binder/IMemory.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkImageEncoder.h>
+#include <SkBitmap.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+    if (argc != 2) {
+        printf("usage: %s path\n", argv[0]);
+        exit(0);
+    }
+
+    const String16 name("SurfaceFlinger");
+    sp<ISurfaceComposer> composer;
+    getService(name, &composer);
+
+    sp<IMemoryHeap> heap;
+    uint32_t w, h;
+    PixelFormat f;
+    status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
+        exit(0);
+    }
+
+    printf("screen capture success: w=%u, h=%u, pixels=%p\n",
+            w, h, heap->getBase());
+
+    printf("saving file as PNG in %s ...\n", argv[1]);
+
+    SkBitmap b;
+    b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    b.setPixels(heap->getBase());
+    SkImageEncoder::EncodeFile(argv[1], b,
+            SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
+
+    return 0;
+}
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 4d8cbf0..7c3508f 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -54,9 +54,9 @@
  */
 public final class CallManager {
 
-    private static final String LOG_TAG ="Phone";
+    private static final String LOG_TAG ="CallManager";
     private static final boolean DBG = true;
-    private static final boolean VDBG = false;
+    private static final boolean VDBG = true;
 
     private static final int EVENT_DISCONNECT = 100;
     private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
@@ -82,7 +82,7 @@
     // Singleton instance
     private static final CallManager INSTANCE = new CallManager();
 
-    // list of registered phones
+    // list of registered phones, which are PhoneBase objs
     private final ArrayList<Phone> mPhones;
 
     // list of supported ringing calls
@@ -97,7 +97,7 @@
     // empty connection list
     private final ArrayList<Connection> emptyConnections = new ArrayList<Connection>();
 
-    // default phone as the first phone registered
+    // default phone as the first phone registered, which is PhoneBase obj
     private Phone mDefaultPhone;
 
     // state registrants
@@ -181,6 +181,46 @@
     }
 
     /**
+     * Get the corresponding PhoneBase obj
+     *
+     * @param phone a Phone object
+     * @return the corresponding PhoneBase obj in Phone if Phone
+     * is a PhoneProxy obj
+     * or the Phone itself if Phone is not a PhoneProxy obj
+     */
+    private static Phone getPhoneBase(Phone phone) {
+        if (phone instanceof PhoneProxy) {
+            return phone.getForegroundCall().getPhone();
+        }
+        return phone;
+    }
+
+    /**
+     * Check if two phones refer to the same PhoneBase obj
+     *
+     * Note: PhoneBase, not PhoneProxy, is to be used inside of CallManager
+     *
+     * Both PhoneBase and PhoneProxy implement Phone interface, so
+     * they have same phone APIs, such as dial(). The real implementation, for
+     * example in GSM,  is in GSMPhone as extend from PhoneBase, so that
+     * foregroundCall.getPhone() returns GSMPhone obj. On the other hand,
+     * PhoneFactory.getDefaultPhone() returns PhoneProxy obj, which has a class
+     * member of GSMPhone.
+     *
+     * So for phone returned by PhoneFacotry, which is used by PhoneApp,
+     *        phone.getForegroundCall().getPhone() != phone
+     * but
+     *        isSamePhone(phone, phone.getForegroundCall().getPhone()) == true
+     *
+     * @param p1 is the first Phone obj
+     * @param p2 is the second Phone obj
+     * @return true if p1 and p2 refer to the same phone
+     */
+    public static boolean isSamePhone(Phone p1, Phone p2) {
+        return (getPhoneBase(p1) == getPhoneBase(p2));
+    }
+
+    /**
      * Returns all the registered phone objects.
      * @return all the registered phone objects.
      */
@@ -198,9 +238,7 @@
         Phone.State s = Phone.State.IDLE;
 
         for (Phone phone : mPhones) {
-            if (phone.getState() == Phone.State.ANSWERING) {
-                return Phone.State.ANSWERING;
-            } else if (phone.getState() == Phone.State.RINGING) {
+            if (phone.getState() == Phone.State.RINGING) {
                 s = Phone.State.RINGING;
             } else if (phone.getState() == Phone.State.OFFHOOK) {
                 if (s == Phone.State.IDLE) s = Phone.State.OFFHOOK;
@@ -246,25 +284,62 @@
 
     /**
      * Register phone to CallManager
-     * @param phone
+     * @param phone to be registered
      * @return true if register successfully
      */
     public boolean registerPhone(Phone phone) {
-        if (phone != null && !mPhones.contains(phone)) {
-            if (mPhones.isEmpty()) {
-                mDefaultPhone = phone;
+        Phone basePhone = getPhoneBase(phone);
+
+        if (basePhone != null && !mPhones.contains(basePhone)) {
+
+            if (VDBG) {
+                Log.d(LOG_TAG, "registerPhone(" +
+                        phone.getPhoneName() + " " + phone + ")");
             }
-            mPhones.add(phone);
-            mRingingCalls.add(phone.getRingingCall());
-            mBackgroundCalls.add(phone.getBackgroundCall());
-            mForegroundCalls.add(phone.getForegroundCall());
-            registerForPhoneStates(phone);
+
+            if (mPhones.isEmpty()) {
+                mDefaultPhone = basePhone;
+            }
+            mPhones.add(basePhone);
+            mRingingCalls.add(basePhone.getRingingCall());
+            mBackgroundCalls.add(basePhone.getBackgroundCall());
+            mForegroundCalls.add(basePhone.getForegroundCall());
+            registerForPhoneStates(basePhone);
             return true;
         }
         return false;
     }
 
     /**
+     * unregister phone from CallManager
+     * @param phone to be unregistered
+     */
+    public void unregisterPhone(Phone phone) {
+        Phone basePhone = getPhoneBase(phone);
+
+        if (basePhone != null && mPhones.contains(basePhone)) {
+
+            if (VDBG) {
+                Log.d(LOG_TAG, "unregisterPhone(" +
+                        phone.getPhoneName() + " " + phone + ")");
+            }
+
+            mPhones.remove(basePhone);
+            mRingingCalls.remove(basePhone.getRingingCall());
+            mBackgroundCalls.remove(basePhone.getBackgroundCall());
+            mForegroundCalls.remove(basePhone.getForegroundCall());
+            unregisterForPhoneStates(basePhone);
+            if (basePhone == mDefaultPhone) {
+                if (mPhones.isEmpty()) {
+                    mDefaultPhone = null;
+                } else {
+                    mDefaultPhone = mPhones.get(0);
+                }
+            }
+        }
+    }
+
+    /**
      * return the default phone or null if no phone available
      */
     public Phone getDefaultPhone() {
@@ -292,39 +367,6 @@
         return getFirstActiveRingingCall().getPhone();
     }
 
-    /**
-     * @return the first answering call
-     */
-    public Call getFirstAnsweringCall() {
-        for (Phone phone : mPhones) {
-            if (phone.getState() == Phone.State.ANSWERING) {
-                return phone.getForegroundCall();
-            }
-        }
-        return null;
-    }
-
-    /**
-     * unregister phone from CallManager
-     * @param phone
-     */
-    public void unregisterPhone(Phone phone) {
-        if (phone != null && mPhones.contains(phone)) {
-            mPhones.remove(phone);
-            mRingingCalls.remove(phone.getRingingCall());
-            mBackgroundCalls.remove(phone.getBackgroundCall());
-            mForegroundCalls.remove(phone.getForegroundCall());
-            unregisterForPhoneStates(phone);
-            if (phone == mDefaultPhone) {
-                if (mPhones.isEmpty()) {
-                    mDefaultPhone = null;
-                } else {
-                    mDefaultPhone = mPhones.get(0);
-                }
-            }
-        }
-    }
-
     public void setAudioMode() {
         Context context = getContext();
         if (context == null) return;
@@ -436,7 +478,8 @@
         Phone ringingPhone = ringingCall.getPhone();
 
         if (VDBG) {
-            Log.d(LOG_TAG, "CallManager.acceptCall " + this);
+            Log.d(LOG_TAG, "acceptCall(" +ringingCall + " from " + ringingCall.getPhone() + ")");
+            Log.d(LOG_TAG, this.toString());
         }
 
         if ( hasActiveFgCall() ) {
@@ -458,6 +501,11 @@
         }
 
         ringingPhone.acceptCall();
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End acceptCall(" +ringingCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
     }
 
     /**
@@ -470,9 +518,19 @@
      * @exception CallStateException when no call is ringing or waiting
      */
     public void rejectCall(Call ringingCall) throws CallStateException {
+        if (VDBG) {
+            Log.d(LOG_TAG, "rejectCall(" +ringingCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
         Phone ringingPhone = ringingCall.getPhone();
 
         ringingPhone.rejectCall();
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End rejectCall(" +ringingCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
     }
 
     /**
@@ -497,6 +555,11 @@
         Phone activePhone = null;
         Phone heldPhone = null;
 
+        if (VDBG) {
+            Log.d(LOG_TAG, "switchHoldingAndActive(" +heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
         if (hasActiveFgCall()) {
             activePhone = getActiveFgCall().getPhone();
         }
@@ -512,6 +575,11 @@
         if (heldPhone != null && heldPhone != activePhone) {
             heldPhone.switchHoldingAndActive();
         }
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End switchHoldingAndActive(" +heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
     }
 
     /**
@@ -526,6 +594,11 @@
         Phone foregroundPhone = null;
         Phone backgroundPhone = null;
 
+        if (VDBG) {
+            Log.d(LOG_TAG, "hangupForegroundResumeBackground(" +heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
         if (hasActiveFgCall()) {
             foregroundPhone = getFgPhone();
             if (heldCall != null) {
@@ -539,6 +612,11 @@
                 }
             }
         }
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End hangupForegroundResumeBackground(" +heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
     }
 
     /**
@@ -571,6 +649,13 @@
      * In these cases, this operation may not be performed.
      */
     public void conference(Call heldCall) throws CallStateException {
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "conference(" +heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
+
         Phone fgPhone = getFgPhone();
         if (fgPhone instanceof SipPhone) {
             ((SipPhone) fgPhone).conference(heldCall);
@@ -579,6 +664,12 @@
         } else {
             throw(new CallStateException("Can't conference foreground and selected background call"));
         }
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End conference(" +heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
     }
 
     /**
@@ -592,19 +683,23 @@
      * handled asynchronously.
      */
     public Connection dial(Phone phone, String dialString) throws CallStateException {
+        Phone basePhone = getPhoneBase(phone);
+        Connection result;
+
         if (VDBG) {
-            Log.d(LOG_TAG, "CallManager.dial( phone=" + phone + ", dialString="+ dialString + ")");
+            Log.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")");
             Log.d(LOG_TAG, this.toString());
         }
+
         if ( hasActiveFgCall() ) {
             Phone activePhone = getActiveFgCall().getPhone();
             boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
 
             if (DBG) {
-                Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + " sameChannel:" + (activePhone == phone));
+                Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + " sameChannel:" + (activePhone == basePhone));
             }
 
-            if (activePhone != phone) {
+            if (activePhone != basePhone) {
                 if (hasBgCall) {
                     Log.d(LOG_TAG, "Hangup");
                     getActiveFgCall().hangup();
@@ -614,7 +709,15 @@
                 }
             }
         }
-        return phone.dial(dialString);
+
+        result = basePhone.dial(dialString);
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End dial(" + basePhone + ", "+ dialString + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
+        return result;
     }
 
     /**
@@ -673,9 +776,20 @@
      * In these cases, this operation may not be performed.
      */
     public void explicitCallTransfer(Call heldCall) throws CallStateException {
+        if (VDBG) {
+            Log.d(LOG_TAG, " explicitCallTransfer(" + heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
         if (canTransfer(heldCall)) {
             heldCall.getPhone().explicitCallTransfer();
         }
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End explicitCallTransfer(" + heldCall + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
     }
 
     /**
@@ -688,6 +802,7 @@
      * @return null if phone doesn't have or support mmi code
      */
     public List<? extends MmiCode> getPendingMmiCodes(Phone phone) {
+        Log.e(LOG_TAG, "getPendingMmiCodes not implemented");
         return null;
     }
 
@@ -700,6 +815,7 @@
      * @return false if phone doesn't support ussd service
      */
     public boolean sendUssdResponse(Phone phone, String ussdMessge) {
+        Log.e(LOG_TAG, "sendUssdResponse not implemented");
         return false;
     }
 
@@ -713,9 +829,19 @@
      */
 
     public void setMute(boolean muted) {
+        if (VDBG) {
+            Log.d(LOG_TAG, " setMute(" + muted + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
         if (hasActiveFgCall()) {
             getActiveFgCall().getPhone().setMute(muted);
         }
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End setMute(" + muted + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
     }
 
     /**
@@ -743,11 +869,23 @@
      *         dtmf tone
      */
     public boolean sendDtmf(char c) {
+        boolean result = false;
+
+        if (VDBG) {
+            Log.d(LOG_TAG, " sendDtmf(" + c + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
         if (hasActiveFgCall()) {
             getActiveFgCall().getPhone().sendDtmf(c);
-            return true;
+            result = true;
         }
-        return false;
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End sendDtmf(" + c + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+        return result;
     }
 
     /**
@@ -760,11 +898,24 @@
      *         dtmf tone
      */
     public boolean startDtmf(char c) {
+        boolean result = false;
+
+        if (VDBG) {
+            Log.d(LOG_TAG, " startDtmf(" + c + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
         if (hasActiveFgCall()) {
             getActiveFgCall().getPhone().sendDtmf(c);
-            return true;
+            result = true;
         }
-        return false;
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End startDtmf(" + c + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
+        return result;
     }
 
     /**
@@ -772,7 +923,17 @@
      * tone or no active call.
      */
     public void stopDtmf() {
+        if (VDBG) {
+            Log.d(LOG_TAG, " stopDtmf()" );
+            Log.d(LOG_TAG, this.toString());
+        }
+
         if (hasActiveFgCall()) getFgPhone().stopDtmf();
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End stopDtmf()");
+            Log.d(LOG_TAG, this.toString());
+        }
     }
 
     /**
@@ -1312,7 +1473,7 @@
      */
     public Call getFirstActiveBgCall() {
         for (Call call : mBackgroundCalls) {
-            if (!call.isIdle()) {
+            if (call.getState() != Call.State.IDLE) {
                 return call;
             }
         }
@@ -1438,70 +1599,91 @@
 
         @Override
         public void handleMessage(Message msg) {
+
             switch (msg.what) {
                 case EVENT_DISCONNECT:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)");
                     mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_PRECISE_CALL_STATE_CHANGED:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)");
                     mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_NEW_RINGING_CONNECTION:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
                     mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_UNKNOWN_CONNECTION:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_UNKNOWN_CONNECTION)");
                     mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_INCOMING_RING:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_INCOMING_RING)");
                     // The event may come from RIL who's not aware of an ongoing fg call
                     if (!hasActiveFgCall()) {
                         mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     }
                     break;
                 case EVENT_RINGBACK_TONE:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RINGBACK_TONE)");
                     mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_IN_CALL_VOICE_PRIVACY_ON:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_ON)");
                     mInCallVoicePrivacyOnRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_IN_CALL_VOICE_PRIVACY_OFF:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_OFF)");
                     mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_CALL_WAITING:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CALL_WAITING)");
                     mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_DISPLAY_INFO:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISPLAY_INFO)");
                     mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_SIGNAL_INFO:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SIGNAL_INFO)");
                     mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_CDMA_OTA_STATUS_CHANGE:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CDMA_OTA_STATUS_CHANGE)");
                     mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_RESEND_INCALL_MUTE:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RESEND_INCALL_MUTE)");
                     mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_MMI_INITIATE:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_INITIATE)");
                     mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_MMI_COMPLETE:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_COMPLETE)");
                     mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_ECM_TIMER_RESET:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_ECM_TIMER_RESET)");
                     mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_SUBSCRIPTION_INFO_READY:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUBSCRIPTION_INFO_READY)");
                     mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_SUPP_SERVICE_FAILED:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUPP_SERVICE_FAILED)");
                     mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_SERVICE_STATE_CHANGED:
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SERVICE_STATE_CHANGED)");
                     mServiceStateChangedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_POST_DIAL_CHARACTER:
                     // we need send the character that is being processed in msg.arg1
                     // so can't use notifyRegistrants()
+                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_POST_DIAL_CHARACTER)");
                     for(int i=0; i < mPostDialCharacterRegistrants.size(); i++) {
                         Message notifyMsg;
                         notifyMsg = ((Registrant)mPostDialCharacterRegistrants.get(i)).messageForRegistrant();
@@ -1520,36 +1702,29 @@
         StringBuilder b = new StringBuilder();
 
         b.append("########### Dump CallManager ############");
-        b.append("\nCM state = " + getState());
+        b.append("\nCallManager state = " + getState());
         call = getActiveFgCall();
-        b.append("\n   - FG call: " + getActiveFgCallState());
+        b.append("\n   - Foreground: " + getActiveFgCallState());
         b.append(" from " + call.getPhone());
         b.append("\n     Conn: ").append(getFgCallConnections());
         call = getFirstActiveBgCall();
-        b.append("\n   - BG call: " + call.getState());
+        b.append("\n   - Background: " + call.getState());
         b.append(" from " + call.getPhone());
         b.append("\n     Conn: ").append(getBgCallConnections());
         call = getFirstActiveRingingCall();
-        b.append("\n   - RINGING call: " +call.getState());
+        b.append("\n   - Ringing: " +call.getState());
         b.append(" from " + call.getPhone());
 
-        b.append("\n");
         for (Phone phone : getAllPhones()) {
             if (phone != null) {
                 b.append("\n Phone: " + phone + ", name = " + phone.getPhoneName()
                         + ", state = " + phone.getState());
                 call = phone.getForegroundCall();
-                b.append("\n   - FG call: ").append(call);
-                b.append("  State: ").append(call.getState());
-                b.append("\n     Conn: ").append(call.getConnections());
+                b.append("\n   - Foreground: ").append(call);
                 call = phone.getBackgroundCall();
-                b.append("\n   - BG call: ").append(call);
-                b.append("  State: ").append(call.getState());
-                b.append("\n     Conn: ").append(call.getConnections());
+                b.append(" Background: ").append(call);
                 call = phone.getRingingCall();
-                b.append("\n   - RINGING call: ").append(call);
-                b.append( "  State: ").append(call.getState());
-                b.append("\n     Conn: ").append(call.getConnections());
+                b.append(" Ringing: ").append(call);
             }
         }
         b.append("\n########## End Dump CallManager ##########");
diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java
index 72c50fc..0d983b5 100644
--- a/telephony/java/com/android/internal/telephony/Connection.java
+++ b/telephony/java/com/android/internal/telephony/Connection.java
@@ -41,6 +41,8 @@
         INVALID_NUMBER,                 /* invalid dial string */
         NUMBER_UNREACHABLE,             /* cannot reach the peer */
         INVALID_CREDENTIALS,            /* invalid credentials */
+        OUT_OF_NETWORK,                 /* calling from out of network is not allowed */
+        SERVER_ERROR,                   /* server error */
         TIMED_OUT,                      /* client timed out */
         LOST_SIGNAL,
         LIMIT_EXCEEDED,                 /* eg GSM ACM limit exceeded */
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 7464cc7..9afade3 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -55,12 +55,10 @@
      * <li>OFFHOOK = The phone is off hook. At least one call
      * exists that is dialing, active or holding and no calls are
      * ringing or waiting.</li>
-     * <li>ANSWERING = The incoming call is picked up but the
-     *  call is not established yet.</li>
      * </ul>
      */
     enum State {
-        IDLE, RINGING, OFFHOOK, ANSWERING;
+        IDLE, RINGING, OFFHOOK;
     };
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 0a87ddb..bceceda 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -27,7 +27,7 @@
 import android.net.sip.SipException;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
-import android.net.sip.SipSessionState;
+import android.net.sip.SipSession;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -74,7 +74,9 @@
 public class SipPhone extends SipPhoneBase {
     private static final String LOG_TAG = "SipPhone";
     private static final boolean LOCAL_DEBUG = true;
-    private static final int SESSION_TIMEOUT = 8; // in seconds
+    private static final int TIMEOUT_MAKE_CALL = 15; // in seconds
+    private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds
+    private static final int TIMEOUT_HOLD_CALL = 15; // in seconds
 
     // A call that is ringing or (call) waiting
     private SipCall ringingCall = new SipCall();
@@ -92,7 +94,7 @@
         foregroundCall = new SipCall();
         backgroundCall = new SipCall();
         mProfile = profile;
-        mSipManager = SipManager.getInstance(context);
+        mSipManager = SipManager.newInstance(context);
 
         // FIXME: what's this for SIP?
         //Change the system property
@@ -143,23 +145,15 @@
             // in case the active/holding call disappeared and this
             // is no longer call waiting
 
-            if (ringingCall.getState() == Call.State.INCOMING) {
+            if ((ringingCall.getState() == Call.State.INCOMING) ||
+                    (ringingCall.getState() == Call.State.WAITING)) {
                 Log.v(LOG_TAG, "acceptCall");
                 // Always unmute when answering a new call
                 setMute(false);
-                // make ringingCall foreground
-                foregroundCall.switchWith(ringingCall);
-                foregroundCall.acceptCall();
-            } else if (ringingCall.getState() == Call.State.WAITING) {
-                setMute(false);
-                switchHoldingAndActive();
-                // make ringingCall foreground
-                foregroundCall.switchWith(ringingCall);
-                foregroundCall.acceptCall();
+                ringingCall.acceptCall();
             } else {
                 throw new CallStateException("phone not ringing");
             }
-            updatePhoneState();
         }
     }
 
@@ -447,18 +441,23 @@
         @Override
         public void hangup() throws CallStateException {
             synchronized (SipPhone.class) {
-                Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this
-                        + " on phone " + getPhone());
-                CallStateException excp = null;
-                for (Connection c : connections) {
-                    try {
-                        c.hangup();
-                    } catch (CallStateException e) {
-                        excp = e;
+                if (state.isAlive()) {
+                    Log.d(LOG_TAG, "hang up call: " + getState() + ": " + this
+                            + " on phone " + getPhone());
+                    CallStateException excp = null;
+                    for (Connection c : connections) {
+                        try {
+                            c.hangup();
+                        } catch (CallStateException e) {
+                            excp = e;
+                        }
                     }
+                    if (excp != null) throw excp;
+                    setState(State.DISCONNECTING);
+                } else {
+                    Log.d(LOG_TAG, "hang up dead call: " + getState() + ": "
+                            + this + " on phone " + getPhone());
                 }
-                if (excp != null) throw excp;
-                setState(State.DISCONNECTING);
             }
         }
 
@@ -482,8 +481,8 @@
         }
 
         void acceptCall() throws CallStateException {
-            if (this != foregroundCall) {
-                throw new CallStateException("acceptCall() in a non-fg call");
+            if (this != ringingCall) {
+                throw new CallStateException("acceptCall() in a non-ringing call");
             }
             if (connections.size() != 1) {
                 throw new CallStateException("acceptCall() in a conf call");
@@ -596,7 +595,10 @@
             // set state to DISCONNECTED only when all conns are disconnected
             if (state != State.DISCONNECTED) {
                 boolean allConnectionsDisconnected = true;
+                Log.v(LOG_TAG, "---check if all connections are disconnected: "
+                        + connections.size());
                 for (Connection c : connections) {
+                    Log.v(LOG_TAG, "   state=" + c.getState() + ": " + c);
                     if (c.getState() != State.DISCONNECTED) {
                         allConnectionsDisconnected = false;
                         break;
@@ -639,6 +641,18 @@
             }
 
             @Override
+            public void onCallEstablished(SipAudioCall call) {
+                onChanged(call);
+                if (mState == Call.State.ACTIVE) call.startAudio();
+            }
+
+            @Override
+            public void onCallHeld(SipAudioCall call) {
+                onChanged(call);
+                if (mState == Call.State.HOLDING) call.startAudio();
+            }
+
+            @Override
             public void onChanged(SipAudioCall call) {
                 synchronized (SipPhone.class) {
                     Call.State newState = getCallStateFrom(call);
@@ -646,7 +660,18 @@
                     if (newState == Call.State.INCOMING) {
                         setState(mOwner.getState()); // INCOMING or WAITING
                     } else {
-                        if (newState == Call.State.ACTIVE) call.startAudio();
+                        if (mOwner == ringingCall) {
+                            if (ringingCall.getState() == Call.State.WAITING) {
+                                try {
+                                    switchHoldingAndActive();
+                                } catch (CallStateException e) {
+                                    // disconnect the call.
+                                    onCallEnded(DisconnectCause.LOCAL);
+                                    return;
+                                }
+                            }
+                            foregroundCall.switchWith(ringingCall);
+                        }
                         setState(newState);
                     }
                     mOwner.onConnectionStateChanged(SipConnection.this);
@@ -686,7 +711,7 @@
 
         void acceptCall() throws CallStateException {
             try {
-                mSipAudioCall.answerCall(SESSION_TIMEOUT);
+                mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL);
             } catch (SipException e) {
                 throw new CallStateException("acceptCall(): " + e);
             }
@@ -703,8 +728,8 @@
 
         void dial() throws SipException {
             setState(Call.State.DIALING);
-            mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile,
-                    mPeer, null, SESSION_TIMEOUT);
+            mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null,
+                    TIMEOUT_MAKE_CALL);
             mSipAudioCall.setRingbackToneEnabled(false);
             mSipAudioCall.setListener(mAdapter);
         }
@@ -712,7 +737,7 @@
         void hold() throws CallStateException {
             setState(Call.State.HOLDING);
             try {
-                mSipAudioCall.holdCall(SESSION_TIMEOUT);
+                mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL);
             } catch (SipException e) {
                 throw new CallStateException("hold(): " + e);
             }
@@ -722,7 +747,7 @@
             mSipAudioCall.setAudioGroup(audioGroup);
             setState(Call.State.ACTIVE);
             try {
-                mSipAudioCall.continueCall(SESSION_TIMEOUT);
+                mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL);
             } catch (SipException e) {
                 throw new CallStateException("unhold(): " + e);
             }
@@ -764,11 +789,13 @@
         public void hangup() throws CallStateException {
             synchronized (SipPhone.class) {
                 Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
-                        + ": on phone " + getPhone().getPhoneName());
+                        + mState + ": on phone " + getPhone().getPhoneName());
                 try {
-                    if (mSipAudioCall != null) mSipAudioCall.endCall();
-                    setState(Call.State.DISCONNECTING);
-                    setDisconnectCause(DisconnectCause.LOCAL);
+                    if (mState.isAlive()) {
+                        if (mSipAudioCall != null) mSipAudioCall.endCall();
+                        setState(Call.State.DISCONNECTING);
+                        setDisconnectCause(DisconnectCause.LOCAL);
+                    }
                 } catch (SipException e) {
                     throw new CallStateException("hangup(): " + e);
                 }
@@ -804,20 +831,20 @@
         if (sipAudioCall.isOnHold()) return Call.State.HOLDING;
         int sessionState = sipAudioCall.getState();
         switch (sessionState) {
-            case SipSessionState.READY_TO_CALL:            return Call.State.IDLE;
-            case SipSessionState.INCOMING_CALL:
-            case SipSessionState.INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
-            case SipSessionState.OUTGOING_CALL:            return Call.State.DIALING;
-            case SipSessionState.OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
-            case SipSessionState.OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
-            case SipSessionState.IN_CALL:                  return Call.State.ACTIVE;
+            case SipSession.State.READY_TO_CALL:            return Call.State.IDLE;
+            case SipSession.State.INCOMING_CALL:
+            case SipSession.State.INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
+            case SipSession.State.OUTGOING_CALL:            return Call.State.DIALING;
+            case SipSession.State.OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
+            case SipSession.State.OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
+            case SipSession.State.IN_CALL:                  return Call.State.ACTIVE;
             default:
                 Log.w(LOG_TAG, "illegal connection state: " + sessionState);
                 return Call.State.DISCONNECTED;
         }
     }
 
-    private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter {
+    private abstract class SipAudioCallAdapter extends SipAudioCall.Listener {
         protected abstract void onCallEnded(Connection.DisconnectCause cause);
         protected abstract void onError(Connection.DisconnectCause cause);
 
@@ -853,8 +880,13 @@
                 case SipErrorCode.INVALID_CREDENTIALS:
                     onError(Connection.DisconnectCause.INVALID_CREDENTIALS);
                     break;
-                case SipErrorCode.SOCKET_ERROR:
+                case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION:
+                    onError(Connection.DisconnectCause.OUT_OF_NETWORK);
+                    break;
                 case SipErrorCode.SERVER_ERROR:
+                    onError(Connection.DisconnectCause.SERVER_ERROR);
+                    break;
+                case SipErrorCode.SOCKET_ERROR:
                 case SipErrorCode.CLIENT_ERROR:
                 default:
                     Log.w(LOG_TAG, "error: " + SipErrorCode.toString(errorCode)
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 2efbd17..9098e6f 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -538,8 +538,6 @@
 
         if (getRingingCall().isRinging()) {
             state = State.RINGING;
-        } else if (getForegroundCall().isRinging()) {
-            state = State.ANSWERING;
         } else if (getForegroundCall().isIdle()
                 && getBackgroundCall().isIdle()) {
             state = State.IDLE;
diff --git a/test-runner/src/android/test/mock/MockContentResolver.java b/test-runner/src/android/test/mock/MockContentResolver.java
index ab511f8..26eb8e4 100644
--- a/test-runner/src/android/test/mock/MockContentResolver.java
+++ b/test-runner/src/android/test/mock/MockContentResolver.java
@@ -75,6 +75,12 @@
     /** @hide */
     @Override
     protected IContentProvider acquireProvider(Context context, String name) {
+        return acquireExistingProvider(context, name);
+    }
+
+    /** @hide */
+    @Override
+    protected IContentProvider acquireExistingProvider(Context context, String name) {
 
         /*
          * Gets the content provider from the local map
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 48c4520..5fb09a7 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.PowerManager;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.CheckBox;
 import android.widget.TextView;
 
@@ -38,7 +39,6 @@
     TextView mLog;
     DateFormat mDateFormat;
     IntentFilter mFilter;
-    PowerManager.WakeLock mWakeLock;
     PowerManager.WakeLock mPartialWakeLock;
     SpinThread mThread;
 
@@ -65,24 +65,26 @@
         mFilter.addAction(Intent.ACTION_POWER_CONNECTED);
 
         PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "BatteryWaster");
-        mWakeLock.setReferenceCounted(false);
         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BatteryWaster");
         mPartialWakeLock.setReferenceCounted(false);
     }
 
     @Override
-    public void onPause() {
-        super.onPause();
-        stopRunning();
+    public void onResume() {
+        super.onResume();
+        if (((CheckBox)findViewById(R.id.checkbox)).isChecked()) {
+            startRunning();
+        }
+        if (((CheckBox)findViewById(R.id.checkbox_wake)).isChecked()) {
+            mWaking = true;
+            updateWakeLock();
+        }
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        if (mWakeLock.isHeld()) {
-            mWakeLock.release();
-        }
+        stopRunning();
         if (mPartialWakeLock.isHeld()) {
             mPartialWakeLock.release();
         }
@@ -140,13 +142,9 @@
 
     void updateWakeLock() {
         if (mWasting) {
-            if (!mWakeLock.isHeld()) {
-                mWakeLock.acquire();
-            }
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         } else {
-            if (mWakeLock.isHeld()) {
-                mWakeLock.release();
-            }
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         }
         if (mWaking) {
             if (!mPartialWakeLock.isHeld()) {
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 1078701..438a060 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -60,6 +60,27 @@
     }
 
     private Test[] mTests = new Test[] {
+        new Test("Double Remove") {
+            public void run() {
+                Log.d(TAG, "set 0");
+                mStatusBarManager.setIcon("speakerphone", R.drawable.stat_sys_phone, 0);
+                Log.d(TAG, "remove 1");
+                mStatusBarManager.removeIcon("tty");
+
+                SystemClock.sleep(1000);
+
+                Log.d(TAG, "set 1");
+                mStatusBarManager.setIcon("tty", R.drawable.stat_sys_phone, 0);
+                if (false) {
+                    Log.d(TAG, "set 2");
+                    mStatusBarManager.setIcon("tty", R.drawable.stat_sys_phone, 0);
+                }
+                Log.d(TAG, "remove 2");
+                mStatusBarManager.removeIcon("tty");
+                Log.d(TAG, "set 3");
+                mStatusBarManager.setIcon("speakerphone", R.drawable.stat_sys_phone, 0);
+            }
+        },
         new Test("Hide") {
             public void run() {
                 Window win = getWindow();
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a1bc241..c5aa573 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -45,7 +45,7 @@
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
           mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
-          mMaxResVersion(NULL), mDebugMode(false),
+          mMaxResVersion(NULL), mDebugMode(false), mProduct(NULL),
           mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
@@ -139,6 +139,8 @@
     void setMaxResVersion(const char * val) { mMaxResVersion = val; }
     bool getDebugMode() { return mDebugMode; }
     void setDebugMode(bool val) { mDebugMode = val; }
+    const char* getProduct() const { return mProduct; }
+    void setProduct(const char * val) { mProduct = val; }
 
     /*
      * Set and get the file specification.
@@ -237,6 +239,7 @@
     const char* mCustomPackage;
     const char* mMaxResVersion;
     bool        mDebugMode;
+    const char* mProduct;
 
     /* file specification */
     int         mArgc;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 71c023d..739b01f 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -67,6 +67,7 @@
         "        [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \\\n"
         "        [-S resource-sources [-S resource-sources ...]] "
         "        [-F apk-file] [-J R-file-dir] \\\n"
+        "        [--product product1,product2,...] \\\n"
         "        [raw-files-dir [raw-files-dir] ...]\n"
         "\n"
         "   Package the android resources.  It will read assets and resources that are\n"
@@ -154,6 +155,9 @@
         "       components target the given package.  Useful when used in\n"
         "       conjunction with --rename-manifest-package to fix tests against\n"
         "       a package that has been renamed.\n"
+        "   --product\n"
+        "       Specifies which variant to choose for strings that have\n"
+        "       product variants\n"
         "   --utf16\n"
         "       changes default encoding for resources to UTF-16.  Only useful when API\n"
         "       level is set to 7 or higher where the default encoding is UTF-8.\n");
@@ -484,6 +488,15 @@
                     bundle.setInstrumentationPackageNameOverride(argv[0]);
                 } else if (strcmp(cp, "-auto-add-overlay") == 0) {
                     bundle.setAutoAddOverlay(true);
+                } else if (strcmp(cp, "-product") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--product' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    bundle.setProduct(argv[0]);
                 } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 755b93b..532fb65 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -574,6 +574,7 @@
                         const String16& itemIdent,
                         int32_t curFormat,
                         bool isFormatted,
+                        const String16& product,
                         bool pseudolocalize,
                         const bool overwrite,
                         ResourceTable* outTable)
@@ -606,6 +607,32 @@
     return err;
 }
 
+/*
+ * Returns true if needle is one of the elements in the comma-separated list
+ * haystack, false otherwise.
+ */
+bool isInProductList(const String16& needle, const String16& haystack) {
+    const char16_t *needle2 = needle.string();
+    const char16_t *haystack2 = haystack.string();
+    size_t needlesize = needle.size();
+
+    while (*haystack2 != '\0') {
+        if (strncmp16(haystack2, needle2, needlesize) == 0) {
+            if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
+                return true;
+            }
+        }
+
+        while (*haystack2 != '\0' && *haystack2 != ',') {
+            haystack2++;
+        }
+        if (*haystack2 == ',') {
+            haystack2++;
+        }
+    }
+
+    return false;
+}
 
 status_t parseAndAddEntry(Bundle* bundle,
                         const sp<AaptFile>& in,
@@ -618,6 +645,7 @@
                         bool curIsStyled,
                         int32_t curFormat,
                         bool isFormatted,
+                        const String16& product,
                         bool pseudolocalize,
                         const bool overwrite,
                         ResourceTable* outTable)
@@ -634,6 +662,47 @@
         return err;
     }
 
+    /*
+     * If a product type was specified on the command line
+     * and also in the string, and the two are not the same,
+     * return without adding the string.
+     */
+
+    const char *bundleProduct = bundle->getProduct();
+    if (bundleProduct == NULL) {
+        bundleProduct = "";
+    }
+
+    if (product.size() != 0) {
+        /*
+         * If the command-line-specified product is empty, only "default"
+         * matches.  Other variants are skipped.  This is so generation
+         * of the R.java file when the product is not known is predictable.
+         */
+
+        if (bundleProduct[0] == '\0') {
+            if (strcmp16(String16("default").string(), product.string()) != 0) {
+                return NO_ERROR;
+            }
+        } else {
+            /*
+             * The command-line product is not empty.
+             * If the product for this string is on the command-line list,
+             * it matches.  "default" also matches, but only if nothing
+             * else has matched already.
+             */
+
+            if (isInProductList(product, String16(bundleProduct))) {
+                ;
+            } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
+                       !outTable->hasBagOrEntry(myPackage, curType, ident)) {
+                ;
+            } else {
+                return NO_ERROR;
+            }
+        }
+    }
+
     NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
                  config.language[0], config.language[1],
                  config.country[0], config.country[1],
@@ -713,6 +782,7 @@
     const String16 translatable16("translatable");
     const String16 formatted16("formatted");
     const String16 false16("false");
+    const String16 product16("product");
 
     const String16 myPackage(assets->getPackage());
 
@@ -760,6 +830,7 @@
             bool curIsStyled = false;
             bool curIsPseudolocalizable = false;
             bool curIsFormatted = fileIsTranslatable;
+            String16 curProduct;
             bool localHasErrors = false;
 
             if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
@@ -1157,6 +1228,8 @@
                         translatable.setTo(block.getAttributeStringValue(i, &length));
                     } else if (strcmp16(attr, formatted16.string()) == 0) {
                         formatted.setTo(block.getAttributeStringValue(i, &length));
+                    } else if (strcmp16(attr, product16.string()) == 0) {
+                        curProduct.setTo(block.getAttributeStringValue(i, &length));
                     }
                 }
                 
@@ -1374,7 +1447,7 @@
 
                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
                                 ident, parentIdent, itemIdent, curFormat, curIsFormatted,
-                                false, overwrite, outTable);
+                                curProduct, false, overwrite, outTable);
                         if (err == NO_ERROR) {
                             if (curIsPseudolocalizable && localeIsDefined(curParams)
                                     && bundle->getPseudolocalize()) {
@@ -1383,7 +1456,7 @@
                                 block.setPosition(parserPosition);
                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
                                         curType, ident, parentIdent, itemIdent, curFormat,
-                                        curIsFormatted, true, overwrite, outTable);
+                                        curIsFormatted, curProduct, true, overwrite, outTable);
 #endif
                             }
                         } 
@@ -1407,7 +1480,7 @@
 
                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
                         *curTag, curIsStyled, curFormat, curIsFormatted,
-                        false, overwrite, outTable);
+                        curProduct, false, overwrite, outTable);
 
                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
                     hasErrors = localHasErrors = true;
@@ -1419,7 +1492,8 @@
                         block.setPosition(parserPosition);
                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
                                 ident, *curTag, curIsStyled, curFormat,
-                                curIsFormatted, true, overwrite, outTable);
+                                curIsFormatted, curProduct,
+                                true, overwrite, outTable);
                         if (err != NO_ERROR) {
                             hasErrors = localHasErrors = true;
                         }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
index cfab90a..d89dba9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
@@ -43,6 +43,12 @@
     }
 
     @Override
+    public IContentProvider acquireExistingProvider(Context c, String name) {
+        // ignore
+        return null;
+    }
+
+    @Override
     public boolean releaseProvider(IContentProvider icp) {
         // ignore
         return false;
diff --git a/tools/obbtool/mkobb.sh b/tools/obbtool/mkobb.sh
new file mode 100755
index 0000000..f4cae9a1
--- /dev/null
+++ b/tools/obbtool/mkobb.sh
@@ -0,0 +1,260 @@
+#!/bin/bash
+#
+# 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.
+#
+
+# mkobb.sh - Creates OBB files on Linux machines
+
+# Directory where we should temporarily mount the OBB loopback to copy files
+MOUNTDIR=/tmp
+
+# Presets. Changing these will probably break your OBB on the device
+CRYPTO=blowfish
+FS=vfat
+MKFS=mkfs.vfat
+LOSETUP=losetup
+BLOCK_SIZE=512
+SLOP=512 # Amount of filesystem slop in ${BLOCK_SIZE} blocks
+
+find_binaries() {
+    MKFSBIN=`which ${MKFS}`
+    LOSETUPBIN=`which ${LOSETUP}`
+    MOUNTBIN=`which mount`
+    UMOUNTBIN=`which umount`
+    DDBIN=`which dd`
+    RSYNCBIN=`which rsync`
+}
+
+check_prereqs() {
+    if [ "`uname -s`x" != "Linuxx" ]; then \
+        echo "ERROR: This script only works on Linux!"
+        exit 1
+    fi
+
+    if ! egrep -q "^cryptoloop " /proc/modules; then \
+        echo "ERROR: Could not find cryptoloop in the kernel."
+        echo "Perhaps you need to: modprobe cryptoloop"
+        exit 1
+    fi
+
+    if ! egrep -q "name\s*:\s*${CRYPTO}$" /proc/crypto; then \
+        echo "ERROR: Could not find crypto \`${CRYPTO}' in the kernel."
+        echo "Perhaps you need to: modprobe ${CRYPTO}"
+        exit 1
+    fi
+
+    if ! egrep -q "^\s*${FS}$" /proc/filesystems; then \
+        echo "ERROR: Could not find filesystem \`${FS}' in the kernel."
+        echo "Perhaps you need to: modprobe ${FS}"
+        exit 1
+    fi
+
+    if [ "${MKFSBIN}x" = "x" ]; then \
+        echo "ERROR: Could not find ${MKFS} in your path!"
+        exit 1
+    elif [ ! -x "${MKFSBIN}" ]; then \
+        echo "ERROR: ${MKFSBIN} is not executable!"
+        exit 1
+    fi
+
+    if [ "${LOSETUPBIN}x" = "x" ]; then \
+        echo "ERROR: Could not find ${LOSETUP} in your path!"
+        exit 1
+    elif [ ! -x "${LOSETUPBIN}" ]; then \
+        echo "ERROR: ${LOSETUPBIN} is not executable!"
+        exit 1
+    fi
+}
+
+cleanup() {
+    if [ "${loopdev}x" != "x" ]; then \
+        ${LOSETUPBIN} -d ${loopdev}
+    fi
+}
+
+hidden_prompt() {
+    unset output
+    prompt="$1"
+    outvar="$2"
+    while read -s -n 1 -p "$prompt" c; do \
+        if [ "x$c" = "x" ]; then \
+            break
+        fi
+        prompt='*'
+        output="${output}${c}"
+    done
+    echo
+    eval $outvar="$output"
+    unset output
+}
+
+read_key() {
+    hidden_prompt "        Encryption key: " key
+
+    if [ "${key}x" = "x" ]; then \
+        echo "ERROR: An empty key is not allowed!"
+        exit 1
+    fi
+
+    hidden_prompt "Encryption key (again): " key2
+
+    if [ "${key}x" != "${key2}x" ]; then \
+        echo "ERROR: Encryption keys do not match!"
+        exit 1
+    fi
+}
+
+onexit() {
+    if [ "x${temp_mount}" != "x" ]; then \
+        ${UMOUNTBIN} ${temp_mount}
+        rmdir ${temp_mount}
+    fi
+    if [ "x${loop_dev}" != "x" ]; then \
+        ${LOSETUPBIN} -d ${loop_dev}
+    fi
+    if [ "x${tempfile}" != "x" -a -f "${tempfile}" ]; then \
+        rm -f ${tempfile}
+    fi
+    if [ "x${keyfile}" != "x" -a -f "${keyfile}" ]; then \
+        rm -f ${keyfile}
+    fi
+    echo "Fatal error."
+    exit 1
+}
+
+usage() {
+    echo "mkobb.sh -- Create OBB files for use on Android"
+    echo ""
+    echo " -c             Use an encrypted OBB; must specify key"
+    echo " -d <directory> Use <directory> as input for OBB files"
+    echo " -k <key>       Use <key> to encrypt OBB file"
+    echo " -K             Prompt for key to encrypt OBB file"
+    echo " -o <filename>  Write OBB file out to <filename>"
+    echo " -v             Verbose mode"
+    echo " -h             Help; this usage screen"
+}
+
+find_binaries
+check_prereqs
+
+use_crypto=0
+
+args=`getopt -o cd:hk:Ko:v -- "$@"`
+eval set -- "$args"
+
+while true; do \
+    case "$1" in
+        -c) use_crypto=1; shift;;
+        -d) directory=$2; shift 2;;
+        -h) usage; exit 1;;
+        -k) key=$2; shift 2;;
+        -K) prompt_key=1; shift;;
+        -v) verbose=1; shift;;
+        -o) filename=$2; shift 2;;
+        --) shift; break;;
+        *) echo "ERROR: Invalid argument in option parsing! Cannot recover. Ever."; exit 1;;
+    esac
+done
+
+if [ "${directory}x" = "x" -o ! -d "${directory}" ]; then \
+    echo "ERROR: Must specify valid input directory"
+    echo ""
+    usage
+    exit 1;
+fi
+
+if [ "${filename}x" = "x" ]; then \
+    echo "ERROR: Must specify filename"
+    echo ""
+    usage
+    exit 1;
+fi
+
+if [ ${use_crypto} -eq 1 -a "${key}x" = "x" -a 0${prompt_key} -eq 0 ]; then \
+    echo "ERROR: Crypto desired, but no key supplied or requested to prompt for."
+    exit 1
+fi
+
+if [ 0${prompt_key} -eq 1 ]; then \
+    read_key
+fi
+
+outdir=`dirname ${filename}`
+if [ ! -d "${outdir}" ]; then \
+    echo "ERROR: Output directory does not exist: ${outdir}"
+    exit 1
+fi
+
+# Make sure we clean up any stuff we create from here on during error conditions
+trap onexit ERR
+
+tempfile=$(tempfile -d ${outdir}) || ( echo "ERROR: couldn't create temporary file in ${outdir}"; exit 1 )
+
+block_count=`du --apparent-size --block-size=512 ${directory} | awk '{ print $1; }'`
+if [ $? -ne 0 ]; then \
+    echo "ERROR: Couldn't read size of input directory ${directory}"
+    exit 1
+fi
+
+echo "Creating temporary file..."
+${DDBIN} if=/dev/zero of=${tempfile} bs=${BLOCK_SIZE} count=$((${block_count} + ${SLOP})) > /dev/null 2>&1
+if [ $? -ne 0 ]; then \
+    echo "ERROR: creating temporary file: $?"
+fi
+
+loop_dev=$(${LOSETUPBIN} -f) || ( echo "ERROR: losetup wouldn't tell us the next unused device"; exit 1 )
+
+if [ ${use_crypto} -eq 1 ]; then \
+    keyfile=$(tempfile -d ${outdir}) || ( echo "ERROR: could not create temporary key file"; exit 1 )
+    ${LOSETUPBIN} -p 5 -e ${CRYPTO} ${loop_dev} ${tempfile} 5< ${keyfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
+    rm -f ${keyfile}
+else \
+    ${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
+fi
+
+#
+# Create the filesystem
+#
+echo ""
+${MKFSBIN} -I ${loop_dev}
+echo ""
+
+#
+# Make the temporary mount point and mount it
+#
+temp_mount="${MOUNTDIR}/${RANDOM}"
+mkdir ${temp_mount}
+${MOUNTBIN} -t ${FS} -o loop ${loop_dev} ${temp_mount}
+
+#
+# rsync the files!
+#
+echo "Copying files:"
+${RSYNCBIN} -av --no-owner --no-group ${directory}/ ${temp_mount}/
+echo ""
+
+echo "Successfully created \`${filename}'"
+
+#
+# Undo all the temporaries
+#
+umount ${temp_mount}
+rmdir ${temp_mount}
+${LOSETUPBIN} -d ${loop_dev}
+mv ${tempfile} ${filename}
+
+trap - ERR
+
+exit 0
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index 89e6aa9..4851a46 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -16,41 +16,133 @@
 
 package android.net.rtp;
 
-/** @hide */
+import java.util.Arrays;
+
+/**
+ * This class defines a collection of audio codecs to be used with
+ * {@link AudioStream}s. Their parameters are designed to be exchanged using
+ * Session Description Protocol (SDP). Most of the values listed here can be
+ * found in RFC 3551, while others are described in separated standards.
+ *
+ * <p>Few simple configurations are defined as public static instances for the
+ * convenience of direct uses. More complicated ones could be obtained using
+ * {@link #getCodec(int, String, String)}. For example, one can use the
+ * following snippet to create a mode-1-only AMR codec.</p>
+ * <pre>
+ * AudioCodec codec = AudioCodec.getCodec(100, "AMR/8000", "mode-set=1");
+ * </pre>
+ *
+ * @see AudioStream
+ * @hide
+ */
 public class AudioCodec {
-    public static final AudioCodec ULAW = new AudioCodec("PCMU", 8000, 160, 0);
-    public static final AudioCodec ALAW = new AudioCodec("PCMA", 8000, 160, 8);
+    /**
+     * The RTP payload type of the encoding.
+     */
+    public final int type;
 
     /**
-     * Returns system supported codecs.
+     * The encoding parameters to be used in the corresponding SDP attribute.
      */
-    public static AudioCodec[] getSystemSupportedCodecs() {
-        return new AudioCodec[] {AudioCodec.ULAW, AudioCodec.ALAW};
+    public final String rtpmap;
+
+    /**
+     * The format parameters to be used in the corresponding SDP attribute.
+     */
+    public final String fmtp;
+
+    /**
+     * G.711 u-law audio codec.
+     */
+    public static final AudioCodec PCMU = new AudioCodec(0, "PCMU/8000", null);
+
+    /**
+     * G.711 a-law audio codec.
+     */
+    public static final AudioCodec PCMA = new AudioCodec(8, "PCMA/8000", null);
+
+    /**
+     * GSM Full-Rate audio codec, also known as GSM-FR, GSM 06.10, GSM, or
+     * simply FR.
+     */
+    public static final AudioCodec GSM = new AudioCodec(3, "GSM/8000", null);
+
+    /**
+     * GSM Enhanced Full-Rate audio codec, also known as GSM-EFR, GSM 06.60, or
+     * simply EFR.
+     */
+    public static final AudioCodec GSM_EFR = new AudioCodec(96, "GSM-EFR/8000", null);
+
+    /**
+     * Adaptive Multi-Rate narrowband audio codec, also known as AMR or AMR-NB.
+     * Currently CRC, robust sorting, and interleaving are not supported. See
+     * more details about these features in RFC 4867.
+     */
+    public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
+
+    // TODO: add rest of the codecs when the native part is done.
+    private static final AudioCodec[] sCodecs = {PCMU, PCMA};
+
+    private AudioCodec(int type, String rtpmap, String fmtp) {
+        this.type = type;
+        this.rtpmap = rtpmap;
+        this.fmtp = fmtp;
     }
 
     /**
-     * Returns the codec instance if it is supported by the system.
+     * Returns system supported audio codecs.
+     */
+    public static AudioCodec[] getCodecs() {
+        return Arrays.copyOf(sCodecs, sCodecs.length);
+    }
+
+    /**
+     * Creates an AudioCodec according to the given configuration.
      *
-     * @param name name of the codec
-     * @return the matched codec or null if the codec name is not supported by
-     *      the system
+     * @param type The payload type of the encoding defined in RTP/AVP.
+     * @param rtpmap The encoding parameters specified in the corresponding SDP
+     *     attribute, or null if it is not available.
+     * @param fmtp The format parameters specified in the corresponding SDP
+     *     attribute, or null if it is not available.
+     * @return The configured AudioCodec or {@code null} if it is not supported.
      */
-    public static AudioCodec getSystemSupportedCodec(String name) {
-        for (AudioCodec codec : getSystemSupportedCodecs()) {
-            if (codec.name.equals(name)) return codec;
+    public static AudioCodec getCodec(int type, String rtpmap, String fmtp) {
+        if (type < 0 || type > 127) {
+            return null;
         }
-        return null;
-    }
 
-    public final String name;
-    public final int sampleRate;
-    public final int sampleCount;
-    public final int defaultType;
+        AudioCodec hint = null;
+        if (rtpmap != null) {
+            String clue = rtpmap.trim().toUpperCase();
+            for (AudioCodec codec : sCodecs) {
+                if (clue.startsWith(codec.rtpmap)) {
+                    String channels = clue.substring(codec.rtpmap.length());
+                    if (channels.length() == 0 || channels.equals("/1")) {
+                        hint = codec;
+                    }
+                    break;
+                }
+            }
+        } else if (type < 96) {
+            for (AudioCodec codec : sCodecs) {
+                if (type == codec.type) {
+                    hint = codec;
+                    rtpmap = codec.rtpmap;
+                    break;
+                }
+            }
+        }
 
-    private AudioCodec(String name, int sampleRate, int sampleCount, int defaultType) {
-        this.name = name;
-        this.sampleRate = sampleRate;
-        this.sampleCount = sampleCount;
-        this.defaultType = defaultType;
+        if (hint == null) {
+            return null;
+        }
+        if (hint == AMR && fmtp != null) {
+            String clue = fmtp.toLowerCase();
+            if (clue.contains("crc=1") || clue.contains("robust-sorting=1") ||
+                    clue.contains("interleaving=")) {
+                return null;
+            }
+        }
+        return new AudioCodec(type, rtpmap, fmtp);
     }
 }
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index 37cc121..43a3827 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -20,13 +20,63 @@
 import java.util.Map;
 
 /**
+ * An AudioGroup acts as a router connected to the speaker, the microphone, and
+ * {@link AudioStream}s. Its pipeline has four steps. First, for each
+ * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
+ * packets and stores in its buffer. Then, if the microphone is enabled,
+ * processes the recorded audio and stores in its buffer. Third, if the speaker
+ * is enabled, mixes and playbacks buffers of all AudioStreams. Finally, for
+ * each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
+ * buffers and sends back the encoded packets. An AudioGroup does nothing if
+ * there is no AudioStream in it.
+ *
+ * <p>Few things must be noticed before using these classes. The performance is
+ * highly related to the system load and the network bandwidth. Usually a
+ * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
+ * bandwidth, and vise versa. Using two AudioStreams at the same time not only
+ * doubles the load but also the bandwidth. The condition varies from one device
+ * to another, and developers must choose the right combination in order to get
+ * the best result.
+ *
+ * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
+ * example, a Voice over IP (VoIP) application might want to put a conference
+ * call on hold in order to make a new call but still allow people in the
+ * previous call to talk to each other. This can be done easily using two
+ * AudioGroups, but there are some limitations. Since the speaker and the
+ * microphone are shared globally, only one AudioGroup is allowed to run in
+ * modes other than {@link #MODE_ON_HOLD}. In addition, before adding an
+ * AudioStream into an AudioGroup, one should always put all other AudioGroups
+ * into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly
+ * initialized.
+ * @hide
  */
-/** @hide */
 public class AudioGroup {
+    /**
+     * This mode is similar to {@link #MODE_NORMAL} except the speaker and
+     * the microphone are disabled.
+     */
     public static final int MODE_ON_HOLD = 0;
+
+    /**
+     * This mode is similar to {@link #MODE_NORMAL} except the microphone is
+     * muted.
+     */
     public static final int MODE_MUTED = 1;
+
+    /**
+     * This mode indicates that the speaker, the microphone, and all
+     * {@link AudioStream}s in the group are enabled. First, the packets
+     * received from the streams are decoded and mixed with the audio recorded
+     * from the microphone. Then, the results are played back to the speaker,
+     * encoded and sent back to each stream.
+     */
     public static final int MODE_NORMAL = 2;
-    public static final int MODE_EC_ENABLED = 3;
+
+    /**
+     * This mode is similar to {@link #MODE_NORMAL} except the echo suppression
+     * is enabled. It should be only used when the speaker phone is on.
+     */
+    public static final int MODE_ECHO_SUPPRESSION = 3;
 
     private final Map<AudioStream, Integer> mStreams;
     private int mMode = MODE_ON_HOLD;
@@ -36,23 +86,42 @@
         System.loadLibrary("rtp_jni");
     }
 
+    /**
+     * Creates an empty AudioGroup.
+     */
     public AudioGroup() {
         mStreams = new HashMap<AudioStream, Integer>();
     }
 
+    /**
+     * Returns the current mode.
+     */
     public int getMode() {
         return mMode;
     }
 
+    /**
+     * Changes the current mode. It must be one of {@link #MODE_ON_HOLD},
+     * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and
+     * {@link #MODE_ECHO_SUPPRESSION}.
+     *
+     * @param mode The mode to change to.
+     * @throws IllegalArgumentException if the mode is invalid.
+     */
     public synchronized native void setMode(int mode);
 
-    synchronized void add(AudioStream stream, AudioCodec codec, int codecType, int dtmfType) {
+    private native void add(int mode, int socket, String remoteAddress,
+            int remotePort, String codecSpec, int dtmfType);
+
+    synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) {
         if (!mStreams.containsKey(stream)) {
             try {
                 int socket = stream.dup();
+                String codecSpec = String.format("%d %s %s", codec.type,
+                        codec.rtpmap, codec.fmtp);
                 add(stream.getMode(), socket,
-                        stream.getRemoteAddress().getHostAddress(), stream.getRemotePort(),
-                        codec.name, codec.sampleRate, codec.sampleCount, codecType, dtmfType);
+                        stream.getRemoteAddress().getHostAddress(),
+                        stream.getRemotePort(), codecSpec, dtmfType);
                 mStreams.put(stream, socket);
             } catch (NullPointerException e) {
                 throw new IllegalStateException(e);
@@ -60,8 +129,7 @@
         }
     }
 
-    private native void add(int mode, int socket, String remoteAddress, int remotePort,
-            String codecName, int sampleRate, int sampleCount, int codecType, int dtmfType);
+    private native void remove(int socket);
 
     synchronized void remove(AudioStream stream) {
         Integer socket = mStreams.remove(stream);
@@ -70,8 +138,6 @@
         }
     }
 
-    private native void remove(int socket);
-
     /**
      * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
      * only event {@code 0} to {@code 15} are supported.
@@ -80,13 +146,16 @@
      */
     public native synchronized void sendDtmf(int event);
 
-    public synchronized void reset() {
+    /**
+     * Removes every {@link AudioStream} in this group.
+     */
+    public synchronized void clear() {
         remove(-1);
     }
 
     @Override
     protected void finalize() throws Throwable {
-        reset();
+        clear();
         super.finalize();
     }
 }
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index a955fd2..e5197ce 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -20,12 +20,27 @@
 import java.net.SocketException;
 
 /**
- * AudioStream represents a RTP stream carrying audio payloads.
+ * An AudioStream is a {@link RtpStream} which carrys audio payloads over
+ * Real-time Transport Protocol (RTP). Two different classes are developed in
+ * order to support various usages such as audio conferencing. An AudioStream
+ * represents a remote endpoint which consists of a network mapping and a
+ * configured {@link AudioCodec}. On the other side, An {@link AudioGroup}
+ * represents a local endpoint which mixes all the AudioStreams and optionally
+ * interacts with the speaker and the microphone at the same time. The simplest
+ * usage includes one for each endpoints. For other combinations, users should
+ * be aware of the limitations described in {@link AudioGroup}.
+ *
+ * <p>An AudioStream becomes busy when it joins an AudioGroup. In this case most
+ * of the setter methods are disabled. This is designed to ease the task of
+ * managing native resources. One can always make an AudioStream leave its
+ * AudioGroup by calling {@link #join(AudioGroup)} with {@code null} and put it
+ * back after the modification is done.
+ *
+ * @see AudioGroup
+ * @hide
  */
-/** @hide */
 public class AudioStream extends RtpStream {
     private AudioCodec mCodec;
-    private int mCodecType = -1;
     private int mDtmfType = -1;
     private AudioGroup mGroup;
 
@@ -42,7 +57,8 @@
     }
 
     /**
-     * Returns {@code true} if the stream already joined an {@link AudioGroup}.
+     * Returns {@code true} if the stream has already joined an
+     * {@link AudioGroup}.
      */
     @Override
     public final boolean isBusy() {
@@ -52,7 +68,7 @@
     /**
      * Returns the joined {@link AudioGroup}.
      */
-    public AudioGroup getAudioGroup() {
+    public AudioGroup getGroup() {
         return mGroup;
     }
 
@@ -74,35 +90,45 @@
             mGroup = null;
         }
         if (group != null) {
-            group.add(this, mCodec, mCodecType, mDtmfType);
+            group.add(this, mCodec, mDtmfType);
             mGroup = group;
         }
     }
 
     /**
-     * Sets the {@link AudioCodec} and its RTP payload type. According to RFC
-     * 3551, the type must be in the range of 0 and 127, where 96 and above are
-     * dynamic types. For codecs with static mappings (non-negative
-     * {@link AudioCodec#defaultType}), assigning a different non-dynamic type
-     * is disallowed.
+     * Returns the {@link AudioCodec}, or {@code null} if it is not set.
+     *
+     * @see #setCodec(AudioCodec)
+     */
+    public AudioCodec getCodec() {
+        return mCodec;
+    }
+
+    /**
+     * Sets the {@link AudioCodec}.
      *
      * @param codec The AudioCodec to be used.
-     * @param type The RTP payload type.
-     * @throws IllegalArgumentException if the type is invalid or used by DTMF.
+     * @throws IllegalArgumentException if its type is used by DTMF.
      * @throws IllegalStateException if the stream is busy.
      */
-    public void setCodec(AudioCodec codec, int type) {
+    public void setCodec(AudioCodec codec) {
         if (isBusy()) {
             throw new IllegalStateException("Busy");
         }
-        if (type < 0 || type > 127 || (type != codec.defaultType && type < 96)) {
-            throw new IllegalArgumentException("Invalid type");
-        }
-        if (type == mDtmfType) {
+        if (codec.type == mDtmfType) {
             throw new IllegalArgumentException("The type is used by DTMF");
         }
         mCodec = codec;
-        mCodecType = type;
+    }
+
+    /**
+     * Returns the RTP payload type for dual-tone multi-frequency (DTMF) digits,
+     * or {@code -1} if it is not enabled.
+     *
+     * @see #setDtmfType(int)
+     */
+    public int getDtmfType() {
+        return mDtmfType;
     }
 
     /**
@@ -111,7 +137,7 @@
      * certain tasks, such as second-stage dialing. According to RFC 2833, the
      * RTP payload type for DTMF is assigned dynamically, so it must be in the
      * range of 96 and 127. One can use {@code -1} to disable DTMF and free up
-     * the previous assigned value. This method cannot be called when the stream
+     * the previous assigned type. This method cannot be called when the stream
      * already joined an {@link AudioGroup}.
      *
      * @param type The RTP payload type to be used or {@code -1} to disable it.
@@ -127,7 +153,7 @@
             if (type < 96 || type > 127) {
                 throw new IllegalArgumentException("Invalid type");
             }
-            if (type == mCodecType) {
+            if (type == mCodec.type) {
                 throw new IllegalArgumentException("The type is used by codec");
             }
         }
diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java
index ef5ca17..23fb258 100644
--- a/voip/java/android/net/rtp/RtpStream.java
+++ b/voip/java/android/net/rtp/RtpStream.java
@@ -22,13 +22,25 @@
 import java.net.SocketException;
 
 /**
- * RtpStream represents a base class of media streams running over
- * Real-time Transport Protocol (RTP).
+ * RtpStream represents the base class of streams which send and receive network
+ * packets with media payloads over Real-time Transport Protocol (RTP).
+ * @hide
  */
-/** @hide */
 public class RtpStream {
+    /**
+     * This mode indicates that the stream sends and receives packets at the
+     * same time. This is the initial mode for new streams.
+     */
     public static final int MODE_NORMAL = 0;
+
+    /**
+     * This mode indicates that the stream only sends packets.
+     */
     public static final int MODE_SEND_ONLY = 1;
+
+    /**
+     * This mode indicates that the stream only receives packets.
+     */
     public static final int MODE_RECEIVE_ONLY = 2;
 
     private final InetAddress mLocalAddress;
@@ -89,15 +101,16 @@
     }
 
     /**
-     * Returns {@code true} if the stream is busy. This method is intended to be
-     * overridden by subclasses.
+     * Returns {@code true} if the stream is busy. In this case most of the
+     * setter methods are disabled. This method is intended to be overridden
+     * by subclasses.
      */
     public boolean isBusy() {
         return false;
     }
 
     /**
-     * Returns the current mode. The initial mode is {@link #MODE_NORMAL}.
+     * Returns the current mode.
      */
     public int getMode() {
         return mMode;
@@ -123,7 +136,8 @@
     }
 
     /**
-     * Associates with a remote host.
+     * Associates with a remote host. This defines the destination of the
+     * outgoing packets.
      *
      * @param address The network address of the remote host.
      * @param port The network port of the remote host.
diff --git a/voip/java/android/net/sip/SdpSessionDescription.java b/voip/java/android/net/sip/SdpSessionDescription.java
deleted file mode 100644
index f6ae837..0000000
--- a/voip/java/android/net/sip/SdpSessionDescription.java
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import gov.nist.javax.sdp.SessionDescriptionImpl;
-import gov.nist.javax.sdp.fields.AttributeField;
-import gov.nist.javax.sdp.fields.ConnectionField;
-import gov.nist.javax.sdp.fields.MediaField;
-import gov.nist.javax.sdp.fields.OriginField;
-import gov.nist.javax.sdp.fields.ProtoVersionField;
-import gov.nist.javax.sdp.fields.SessionNameField;
-import gov.nist.javax.sdp.fields.TimeField;
-import gov.nist.javax.sdp.parser.SDPAnnounceParser;
-
-import android.util.Log;
-
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Vector;
-import javax.sdp.Connection;
-import javax.sdp.MediaDescription;
-import javax.sdp.SdpException;
-
-/**
- * A session description that follows SDP (Session Description Protocol).
- * Refer to <a href="http://tools.ietf.org/html/rfc4566">RFC 4566</a>.
- * @hide
- */
-public class SdpSessionDescription extends SessionDescription {
-    private static final String TAG = "SDP";
-    private static final String AUDIO = "audio";
-    private static final String RTPMAP = "rtpmap";
-    private static final String PTIME = "ptime";
-    private static final String SENDONLY = "sendonly";
-    private static final String RECVONLY = "recvonly";
-    private static final String INACTIVE = "inactive";
-
-    private SessionDescriptionImpl mSessionDescription;
-
-    /**
-     * The audio codec information parsed from "rtpmap".
-     */
-    public static class AudioCodec {
-        public final int payloadType;
-        public final String name;
-        public final int sampleRate;
-        public final int sampleCount;
-
-        public AudioCodec(int payloadType, String name, int sampleRate,
-                int sampleCount) {
-            this.payloadType = payloadType;
-            this.name = name;
-            this.sampleRate = sampleRate;
-            this.sampleCount = sampleCount;
-        }
-    }
-
-    /**
-     * The builder class used to create an {@link SdpSessionDescription} object.
-     */
-    public static class Builder {
-        private SdpSessionDescription mSdp = new SdpSessionDescription();
-        private SessionDescriptionImpl mSessionDescription;
-
-        public Builder(String sessionName) throws SdpException {
-            mSessionDescription = new SessionDescriptionImpl();
-            mSdp.mSessionDescription = mSessionDescription;
-            try {
-                ProtoVersionField proto = new ProtoVersionField();
-                proto.setVersion(0);
-                mSessionDescription.addField(proto);
-
-                TimeField time = new TimeField();
-                time.setZero();
-                mSessionDescription.addField(time);
-
-                SessionNameField session = new SessionNameField();
-                session.setValue(sessionName);
-                mSessionDescription.addField(session);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-        }
-
-        public Builder setConnectionInfo(String networkType, String addressType,
-                String addr) throws SdpException {
-            try {
-                ConnectionField connection = new ConnectionField();
-                connection.setNetworkType(networkType);
-                connection.setAddressType(addressType);
-                connection.setAddress(addr);
-                mSessionDescription.addField(connection);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder setOrigin(SipProfile user, long sessionId,
-                long sessionVersion, String networkType, String addressType,
-                String address) throws SdpException {
-            try {
-                OriginField origin = new OriginField();
-                origin.setUsername(user.getUserName());
-                origin.setSessionId(sessionId);
-                origin.setSessionVersion(sessionVersion);
-                origin.setAddressType(addressType);
-                origin.setNetworkType(networkType);
-                origin.setAddress(address);
-                mSessionDescription.addField(origin);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder addMedia(String media, int port, int numPorts,
-                String transport, Integer... types) throws SdpException {
-            MediaField field = new MediaField();
-            Vector<Integer> typeVector = new Vector<Integer>();
-            Collections.addAll(typeVector, types);
-            try {
-                field.setMediaType(media);
-                field.setMediaPort(port);
-                field.setPortCount(numPorts);
-                field.setProtocol(transport);
-                field.setMediaFormats(typeVector);
-                mSessionDescription.addField(field);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-           return this;
-        }
-
-        public Builder addMediaAttribute(String type, String name, String value)
-                throws SdpException {
-            try {
-                MediaDescription md = mSdp.getMediaDescription(type);
-                if (md == null) {
-                    throw new SdpException("Should add media first!");
-                }
-                AttributeField attribute = new AttributeField();
-                attribute.setName(name);
-                attribute.setValueAllowNull(value);
-                mSessionDescription.addField(attribute);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder addSessionAttribute(String name, String value)
-                throws SdpException {
-            try {
-                AttributeField attribute = new AttributeField();
-                attribute.setName(name);
-                attribute.setValueAllowNull(value);
-                mSessionDescription.addField(attribute);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        private void throwSdpException(Exception e) throws SdpException {
-            if (e instanceof SdpException) {
-                throw (SdpException) e;
-            } else {
-                throw new SdpException(e.toString(), e);
-            }
-        }
-
-        public String build() {
-            return mSdp.toString();
-        }
-    }
-
-    private SdpSessionDescription() {
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param sdpString an SDP session description to parse
-     */
-    public SdpSessionDescription(String sdpString) throws SdpException {
-        try {
-            mSessionDescription = new SDPAnnounceParser(sdpString).parse();
-        } catch (ParseException e) {
-            throw new SdpException(e.toString(), e);
-        }
-        verify();
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param content a raw SDP session description to parse
-     */
-    public SdpSessionDescription(byte[] content) throws SdpException {
-        this(new String(content));
-    }
-
-    private void verify() throws SdpException {
-        // make sure the syntax is correct over the fields we're interested in
-        Vector<MediaDescription> descriptions = (Vector<MediaDescription>)
-                mSessionDescription.getMediaDescriptions(false);
-        for (MediaDescription md : descriptions) {
-            md.getMedia().getMediaPort();
-            Connection connection = md.getConnection();
-            if (connection != null) connection.getAddress();
-            md.getMedia().getFormats();
-        }
-        Connection connection = mSessionDescription.getConnection();
-        if (connection != null) connection.getAddress();
-    }
-
-    /**
-     * Gets the connection address of the media.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media connection address of the peer
-     */
-    public String getPeerMediaAddress(String type) {
-        try {
-            MediaDescription md = getMediaDescription(type);
-            Connection connection = md.getConnection();
-            if (connection == null) {
-                connection = mSessionDescription.getConnection();
-            }
-            return ((connection == null) ? null : connection.getAddress());
-        } catch (SdpException e) {
-            // should not occur
-            return null;
-        }
-    }
-
-    /**
-     * Gets the connection port number of the media.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media connection port number of the peer
-     */
-    public int getPeerMediaPort(String type) {
-        try {
-            MediaDescription md = getMediaDescription(type);
-            return md.getMedia().getMediaPort();
-        } catch (SdpException e) {
-            // should not occur
-            return -1;
-        }
-    }
-
-    private boolean containsAttribute(String type, String name) {
-        if (name == null) return false;
-        MediaDescription md = getMediaDescription(type);
-        Vector<AttributeField> v = (Vector<AttributeField>)
-                md.getAttributeFields();
-        for (AttributeField field : v) {
-            if (name.equals(field.getAttribute().getName())) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Checks if the media is "sendonly".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is "sendonly"
-     */
-    public boolean isSendOnly(String type) {
-        boolean answer = containsAttribute(type, SENDONLY);
-        Log.d(TAG, "   sendonly? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is "recvonly".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is "recvonly"
-     */
-    public boolean isReceiveOnly(String type) {
-        boolean answer = containsAttribute(type, RECVONLY);
-        Log.d(TAG, "   recvonly? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is in sending; i.e., not "recvonly" and not
-     * "inactive".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is sending
-     */
-    public boolean isSending(String type) {
-        boolean answer = !containsAttribute(type, RECVONLY)
-                && !containsAttribute(type, INACTIVE);
-
-        Log.d(TAG, "   sending? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is in receiving; i.e., not "sendonly" and not
-     * "inactive".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is receiving
-     */
-    public boolean isReceiving(String type) {
-        boolean answer = !containsAttribute(type, SENDONLY)
-                && !containsAttribute(type, INACTIVE);
-        Log.d(TAG, "   receiving? " + answer);
-        return answer;
-    }
-
-    private AudioCodec parseAudioCodec(String rtpmap, int ptime) {
-        String[] ss = rtpmap.split(" ");
-        int payloadType = Integer.parseInt(ss[0]);
-
-        ss = ss[1].split("/");
-        String name = ss[0];
-        int sampleRate = Integer.parseInt(ss[1]);
-        int channelCount = 1;
-        if (ss.length > 2) channelCount = Integer.parseInt(ss[2]);
-        int sampleCount = sampleRate / (1000 / ptime) * channelCount;
-        return new AudioCodec(payloadType, name, sampleRate, sampleCount);
-    }
-
-    /**
-     * Gets the list of audio codecs in this session description.
-     *
-     * @return the list of audio codecs in this session description
-     */
-    public List<AudioCodec> getAudioCodecs() {
-        MediaDescription md = getMediaDescription(AUDIO);
-        if (md == null) return new ArrayList<AudioCodec>();
-
-        // FIXME: what happens if ptime is missing
-        int ptime = 20;
-        try {
-            String value = md.getAttribute(PTIME);
-            if (value != null) ptime = Integer.parseInt(value);
-        } catch (Throwable t) {
-            Log.w(TAG, "getCodecs(): ignored: " + t);
-        }
-
-        List<AudioCodec> codecs = new ArrayList<AudioCodec>();
-        Vector<AttributeField> v = (Vector<AttributeField>)
-                md.getAttributeFields();
-        for (AttributeField field : v) {
-            try {
-                if (RTPMAP.equals(field.getName())) {
-                    AudioCodec codec = parseAudioCodec(field.getValue(), ptime);
-                    if (codec != null) codecs.add(codec);
-                }
-            } catch (Throwable t) {
-                Log.w(TAG, "getCodecs(): ignored: " + t);
-            }
-        }
-        return codecs;
-    }
-
-    /**
-     * Gets the media description of the specified type.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media description of the specified type
-     */
-    public MediaDescription getMediaDescription(String type) {
-        MediaDescription[] all = getMediaDescriptions();
-        if ((all == null) || (all.length == 0)) return null;
-        for (MediaDescription md : all) {
-            String t = md.getMedia().getMedia();
-            if (t.equalsIgnoreCase(type)) return md;
-        }
-        return null;
-    }
-
-    /**
-     * Gets all the media descriptions in this session description.
-     *
-     * @return all the media descriptions in this session description
-     */
-    public MediaDescription[] getMediaDescriptions() {
-        try {
-            Vector<MediaDescription> descriptions = (Vector<MediaDescription>)
-                    mSessionDescription.getMediaDescriptions(false);
-            MediaDescription[] all = new MediaDescription[descriptions.size()];
-            return descriptions.toArray(all);
-        } catch (SdpException e) {
-            Log.e(TAG, "getMediaDescriptions", e);
-        }
-        return null;
-    }
-
-    @Override
-    public String getType() {
-        return "sdp";
-    }
-
-    @Override
-    public byte[] getContent() {
-          return mSessionDescription.toString().getBytes();
-    }
-
-    @Override
-    public String toString() {
-        return mSessionDescription.toString();
-    }
-}
diff --git a/voip/java/android/net/sip/SessionDescription.java b/voip/java/android/net/sip/SessionDescription.java
deleted file mode 100644
index d476f0b..0000000
--- a/voip/java/android/net/sip/SessionDescription.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Abstract class of a session description.
- * @hide
- */
-public abstract class SessionDescription implements Parcelable {
-    /** @hide */
-    public static final Parcelable.Creator<SessionDescription> CREATOR =
-            new Parcelable.Creator<SessionDescription>() {
-                public SessionDescription createFromParcel(Parcel in) {
-                    return new SessionDescriptionImpl(in);
-                }
-
-                public SessionDescription[] newArray(int size) {
-                    return new SessionDescriptionImpl[size];
-                }
-            };
-
-    /**
-     * Gets the type of the session description; e.g., "SDP".
-     *
-     * @return the session description type
-     */
-    public abstract String getType();
-
-    /**
-     * Gets the raw content of the session description.
-     *
-     * @return the content of the session description
-     */
-    public abstract byte[] getContent();
-
-    /** @hide */
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(getType());
-        out.writeByteArray(getContent());
-    }
-
-    /** @hide */
-    public int describeContents() {
-        return 0;
-    }
-
-    private static class SessionDescriptionImpl extends SessionDescription {
-        private String mType;
-        private byte[] mContent;
-
-        SessionDescriptionImpl(Parcel in) {
-            mType = in.readString();
-            mContent = in.createByteArray();
-        }
-
-        @Override
-        public String getType() {
-            return mType;
-        }
-
-        @Override
-        public byte[] getContent() {
-            return mContent;
-        }
-    }
-}
diff --git a/voip/java/android/net/sip/SimpleSessionDescription.java b/voip/java/android/net/sip/SimpleSessionDescription.java
new file mode 100644
index 0000000..29166dc
--- /dev/null
+++ b/voip/java/android/net/sip/SimpleSessionDescription.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.sip;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * An object used to manipulate messages of Session Description Protocol (SDP).
+ * It is mainly designed for the uses of Session Initiation Protocol (SIP).
+ * Therefore, it only handles connection addresses ("c="), bandwidth limits,
+ * ("b="), encryption keys ("k="), and attribute fields ("a="). Currently this
+ * implementation does not support multicast sessions.
+ *
+ * <p>Here is an example code to create a session description.</p>
+ * <pre>
+ * SimpleSessionDescription description = new SimpleSessionDescription(
+ *     System.currentTimeMillis(), "1.2.3.4");
+ * Media media = description.newMedia("audio", 56789, 1, "RTP/AVP");
+ * media.setRtpPayload(0, "PCMU/8000", null);
+ * media.setRtpPayload(8, "PCMA/8000", null);
+ * media.setRtpPayload(127, "telephone-event/8000", "0-15");
+ * media.setAttribute("sendrecv", "");
+ * </pre>
+ * <p>Invoking <code>description.encode()</code> will produce a result like the
+ * one below.</p>
+ * <pre>
+ * v=0
+ * o=- 1284970442706 1284970442709 IN IP4 1.2.3.4
+ * s=-
+ * c=IN IP4 1.2.3.4
+ * t=0 0
+ * m=audio 56789 RTP/AVP 0 8 127
+ * a=rtpmap:0 PCMU/8000
+ * a=rtpmap:8 PCMA/8000
+ * a=rtpmap:127 telephone-event/8000
+ * a=fmtp:127 0-15
+ * a=sendrecv
+ * </pre>
+ * @hide
+ */
+public class SimpleSessionDescription {
+    private final Fields mFields = new Fields("voscbtka");
+    private final ArrayList<Media> mMedia = new ArrayList<Media>();
+
+    /**
+     * Creates a minimal session description from the given session ID and
+     * unicast address. The address is used in the origin field ("o=") and the
+     * connection field ("c="). See {@link SimpleSessionDescription} for an
+     * example of its usage.
+     */
+    public SimpleSessionDescription(long sessionId, String address) {
+        address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
+        mFields.parse("v=0");
+        mFields.parse(String.format("o=- %d %d %s", sessionId,
+                System.currentTimeMillis(), address));
+        mFields.parse("s=-");
+        mFields.parse("t=0 0");
+        mFields.parse("c=" + address);
+    }
+
+    /**
+     * Creates a session description from the given message.
+     *
+     * @throws IllegalArgumentException if message is invalid.
+     */
+    public SimpleSessionDescription(String message) {
+        String[] lines = message.trim().replaceAll(" +", " ").split("[\r\n]+");
+        Fields fields = mFields;
+
+        for (String line : lines) {
+            try {
+                if (line.charAt(1) != '=') {
+                    throw new IllegalArgumentException();
+                }
+                if (line.charAt(0) == 'm') {
+                    String[] parts = line.substring(2).split(" ", 4);
+                    String[] ports = parts[1].split("/", 2);
+                    Media media = newMedia(parts[0], Integer.parseInt(ports[0]),
+                            (ports.length < 2) ? 1 : Integer.parseInt(ports[1]),
+                            parts[2]);
+                    for (String format : parts[3].split(" ")) {
+                        media.setFormat(format, null);
+                    }
+                    fields = media;
+                } else {
+                    fields.parse(line);
+                }
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Invalid SDP: " + line);
+            }
+        }
+    }
+
+    /**
+     * Creates a new media description in this session description.
+     *
+     * @param type The media type, e.g. {@code "audio"}.
+     * @param port The first transport port used by this media.
+     * @param portCount The number of contiguous ports used by this media.
+     * @param protocol The transport protocol, e.g. {@code "RTP/AVP"}.
+     */
+    public Media newMedia(String type, int port, int portCount,
+            String protocol) {
+        Media media = new Media(type, port, portCount, protocol);
+        mMedia.add(media);
+        return media;
+    }
+
+    /**
+     * Returns all the media descriptions in this session description.
+     */
+    public Media[] getMedia() {
+        return mMedia.toArray(new Media[mMedia.size()]);
+    }
+
+    /**
+     * Encodes the session description and all its media descriptions in a
+     * string. Note that the result might be incomplete if a required field
+     * has never been added before.
+     */
+    public String encode() {
+        StringBuilder buffer = new StringBuilder();
+        mFields.write(buffer);
+        for (Media media : mMedia) {
+            media.write(buffer);
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the connection address or {@code null} if it is not present.
+     */
+    public String getAddress() {
+        return mFields.getAddress();
+    }
+
+    /**
+     * Sets the connection address. The field will be removed if the address
+     * is {@code null}.
+     */
+    public void setAddress(String address) {
+        mFields.setAddress(address);
+    }
+
+    /**
+     * Returns the encryption method or {@code null} if it is not present.
+     */
+    public String getEncryptionMethod() {
+        return mFields.getEncryptionMethod();
+    }
+
+    /**
+     * Returns the encryption key or {@code null} if it is not present.
+     */
+    public String getEncryptionKey() {
+        return mFields.getEncryptionKey();
+    }
+
+    /**
+     * Sets the encryption method and the encryption key. The field will be
+     * removed if the method is {@code null}.
+     */
+    public void setEncryption(String method, String key) {
+        mFields.setEncryption(method, key);
+    }
+
+    /**
+     * Returns the types of the bandwidth limits.
+     */
+    public String[] getBandwidthTypes() {
+        return mFields.getBandwidthTypes();
+    }
+
+    /**
+     * Returns the bandwidth limit of the given type or {@code -1} if it is not
+     * present.
+     */
+    public int getBandwidth(String type) {
+        return mFields.getBandwidth(type);
+    }
+
+    /**
+     * Sets the bandwith limit for the given type. The field will be removed if
+     * the value is negative.
+     */
+    public void setBandwidth(String type, int value) {
+        mFields.setBandwidth(type, value);
+    }
+
+    /**
+     * Returns the names of all the attributes.
+     */
+    public String[] getAttributeNames() {
+        return mFields.getAttributeNames();
+    }
+
+    /**
+     * Returns the attribute of the given name or {@code null} if it is not
+     * present.
+     */
+    public String getAttribute(String name) {
+        return mFields.getAttribute(name);
+    }
+
+    /**
+     * Sets the attribute for the given name. The field will be removed if
+     * the value is {@code null}. To set a binary attribute, use an empty
+     * string as the value.
+     */
+    public void setAttribute(String name, String value) {
+        mFields.setAttribute(name, value);
+    }
+
+    /**
+     * This class represents a media description of a session description. It
+     * can only be created by {@link SimpleSessionDescription#newMedia}. Since
+     * the syntax is more restricted for RTP based protocols, two sets of access
+     * methods are implemented. See {@link SimpleSessionDescription} for an
+     * example of its usage.
+     */
+    public static class Media extends Fields {
+        private final String mType;
+        private final int mPort;
+        private final int mPortCount;
+        private final String mProtocol;
+        private ArrayList<String> mFormats = new ArrayList<String>();
+
+        private Media(String type, int port, int portCount, String protocol) {
+            super("icbka");
+            mType = type;
+            mPort = port;
+            mPortCount = portCount;
+            mProtocol = protocol;
+        }
+
+        /**
+         * Returns the media type.
+         */
+        public String getType() {
+            return mType;
+        }
+
+        /**
+         * Returns the first transport port used by this media.
+         */
+        public int getPort() {
+            return mPort;
+        }
+
+        /**
+         * Returns the number of contiguous ports used by this media.
+         */
+        public int getPortCount() {
+            return mPortCount;
+        }
+
+        /**
+         * Returns the transport protocol.
+         */
+        public String getProtocol() {
+            return mProtocol;
+        }
+
+        /**
+         * Returns the media formats.
+         */
+        public String[] getFormats() {
+            return mFormats.toArray(new String[mFormats.size()]);
+        }
+
+        /**
+         * Returns the {@code fmtp} attribute of the given format or
+         * {@code null} if it is not present.
+         */
+        public String getFmtp(String format) {
+            return super.get("a=fmtp:" + format, ' ');
+        }
+
+        /**
+         * Sets a format and its {@code fmtp} attribute. If the attribute is
+         * {@code null}, the corresponding field will be removed.
+         */
+        public void setFormat(String format, String fmtp) {
+            mFormats.remove(format);
+            mFormats.add(format);
+            super.set("a=rtpmap:" + format, ' ', null);
+            super.set("a=fmtp:" + format, ' ', fmtp);
+        }
+
+        /**
+         * Removes a format and its {@code fmtp} attribute.
+         */
+        public void removeFormat(String format) {
+            mFormats.remove(format);
+            super.set("a=rtpmap:" + format, ' ', null);
+            super.set("a=fmtp:" + format, ' ', null);
+        }
+
+        /**
+         * Returns the RTP payload types.
+         */
+        public int[] getRtpPayloadTypes() {
+            int[] types = new int[mFormats.size()];
+            int length = 0;
+            for (String format : mFormats) {
+                try {
+                    types[length] = Integer.parseInt(format);
+                    ++length;
+                } catch (NumberFormatException e) { }
+            }
+            return Arrays.copyOf(types, length);
+        }
+
+        /**
+         * Returns the {@code rtpmap} attribute of the given RTP payload type
+         * or {@code null} if it is not present.
+         */
+        public String getRtpmap(int type) {
+            return super.get("a=rtpmap:" + type, ' ');
+        }
+
+        /**
+         * Returns the {@code fmtp} attribute of the given RTP payload type or
+         * {@code null} if it is not present.
+         */
+        public String getFmtp(int type) {
+            return super.get("a=fmtp:" + type, ' ');
+        }
+
+        /**
+         * Sets a RTP payload type and its {@code rtpmap} and {@code fmtp}
+         * attributes. If any of the attributes is {@code null}, the
+         * corresponding field will be removed. See
+         * {@link SimpleSessionDescription} for an example of its usage.
+         */
+        public void setRtpPayload(int type, String rtpmap, String fmtp) {
+            String format = String.valueOf(type);
+            mFormats.remove(format);
+            mFormats.add(format);
+            super.set("a=rtpmap:" + format, ' ', rtpmap);
+            super.set("a=fmtp:" + format, ' ', fmtp);
+        }
+
+        /**
+         * Removes a RTP payload and its {@code rtpmap} and {@code fmtp}
+         * attributes.
+         */
+        public void removeRtpPayload(int type) {
+            removeFormat(String.valueOf(type));
+        }
+
+        private void write(StringBuilder buffer) {
+            buffer.append("m=").append(mType).append(' ').append(mPort);
+            if (mPortCount != 1) {
+                buffer.append('/').append(mPortCount);
+            }
+            buffer.append(' ').append(mProtocol);
+            for (String format : mFormats) {
+                buffer.append(' ').append(format);
+            }
+            buffer.append("\r\n");
+            super.write(buffer);
+        }
+    }
+
+    /**
+     * This class acts as a set of fields, and the size of the set is expected
+     * to be small. Therefore, it uses a simple list instead of maps. Each field
+     * has three parts: a key, a delimiter, and a value. Delimiters are special
+     * because they are not included in binary attributes. As a result, the
+     * private methods, which are the building blocks of this class, all take
+     * the delimiter as an argument.
+     */
+    private static class Fields {
+        private final String mOrder;
+        private final ArrayList<String> mLines = new ArrayList<String>();
+
+        Fields(String order) {
+            mOrder = order;
+        }
+
+        /**
+         * Returns the connection address or {@code null} if it is not present.
+         */
+        public String getAddress() {
+            String address = get("c", '=');
+            if (address == null) {
+                return null;
+            }
+            String[] parts = address.split(" ");
+            if (parts.length != 3) {
+                return null;
+            }
+            int slash = parts[2].indexOf('/');
+            return (slash < 0) ? parts[2] : parts[2].substring(0, slash);
+        }
+
+        /**
+         * Sets the connection address. The field will be removed if the address
+         * is {@code null}.
+         */
+        public void setAddress(String address) {
+            if (address != null) {
+                address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") +
+                        address;
+            }
+            set("c", '=', address);
+        }
+
+        /**
+         * Returns the encryption method or {@code null} if it is not present.
+         */
+        public String getEncryptionMethod() {
+            String encryption = get("k", '=');
+            if (encryption == null) {
+                return null;
+            }
+            int colon = encryption.indexOf(':');
+            return (colon == -1) ? encryption : encryption.substring(0, colon);
+        }
+
+        /**
+         * Returns the encryption key or {@code null} if it is not present.
+         */
+        public String getEncryptionKey() {
+            String encryption = get("k", '=');
+            if (encryption == null) {
+                return null;
+            }
+            int colon = encryption.indexOf(':');
+            return (colon == -1) ? null : encryption.substring(0, colon + 1);
+        }
+
+        /**
+         * Sets the encryption method and the encryption key. The field will be
+         * removed if the method is {@code null}.
+         */
+        public void setEncryption(String method, String key) {
+            set("k", '=', (method == null || key == null) ?
+                    method : method + ':' + key);
+        }
+
+        /**
+         * Returns the types of the bandwidth limits.
+         */
+        public String[] getBandwidthTypes() {
+            return cut("b=", ':');
+        }
+
+        /**
+         * Returns the bandwidth limit of the given type or {@code -1} if it is
+         * not present.
+         */
+        public int getBandwidth(String type) {
+            String value = get("b=" + type, ':');
+            if (value != null) {
+                try {
+                    return Integer.parseInt(value);
+                } catch (NumberFormatException e) { }
+                setBandwidth(type, -1);
+            }
+            return -1;
+        }
+
+        /**
+         * Sets the bandwith limit for the given type. The field will be removed
+         * if the value is negative.
+         */
+        public void setBandwidth(String type, int value) {
+            set("b=" + type, ':', (value < 0) ? null : String.valueOf(value));
+        }
+
+        /**
+         * Returns the names of all the attributes.
+         */
+        public String[] getAttributeNames() {
+            return cut("a=", ':');
+        }
+
+        /**
+         * Returns the attribute of the given name or {@code null} if it is not
+         * present.
+         */
+        public String getAttribute(String name) {
+            return get("a=" + name, ':');
+        }
+
+        /**
+         * Sets the attribute for the given name. The field will be removed if
+         * the value is {@code null}. To set a binary attribute, use an empty
+         * string as the value.
+         */
+        public void setAttribute(String name, String value) {
+            set("a=" + name, ':', value);
+        }
+
+        private void write(StringBuilder buffer) {
+            for (int i = 0; i < mOrder.length(); ++i) {
+                char type = mOrder.charAt(i);
+                for (String line : mLines) {
+                    if (line.charAt(0) == type) {
+                        buffer.append(line).append("\r\n");
+                    }
+                }
+            }
+        }
+
+        /**
+         * Invokes {@link #set} after splitting the line into three parts.
+         */
+        private void parse(String line) {
+            char type = line.charAt(0);
+            if (mOrder.indexOf(type) == -1) {
+                return;
+            }
+            char delimiter = '=';
+            if (line.startsWith("a=rtpmap:") || line.startsWith("a=fmtp:")) {
+                delimiter = ' ';
+            } else if (type == 'b' || type == 'a') {
+                delimiter = ':';
+            }
+            int i = line.indexOf(delimiter);
+            if (i == -1) {
+                set(line, delimiter, "");
+            } else {
+                set(line.substring(0, i), delimiter, line.substring(i + 1));
+            }
+        }
+
+        /**
+         * Finds the key with the given prefix and returns its suffix.
+         */
+        private String[] cut(String prefix, char delimiter) {
+            String[] names = new String[mLines.size()];
+            int length = 0;
+            for (String line : mLines) {
+                if (line.startsWith(prefix)) {
+                    int i = line.indexOf(delimiter);
+                    if (i == -1) {
+                        i = line.length();
+                    }
+                    names[length] = line.substring(prefix.length(), i);
+                    ++length;
+                }
+            }
+            return Arrays.copyOf(names, length);
+        }
+
+        /**
+         * Returns the index of the key.
+         */
+        private int find(String key, char delimiter) {
+            int length = key.length();
+            for (int i = mLines.size() - 1; i >= 0; --i) {
+                String line = mLines.get(i);
+                if (line.startsWith(key) && (line.length() == length ||
+                        line.charAt(length) == delimiter)) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * Sets the key with the value or removes the key if the value is
+         * {@code null}.
+         */
+        private void set(String key, char delimiter, String value) {
+            int index = find(key, delimiter);
+            if (value != null) {
+                if (value.length() != 0) {
+                    key = key + delimiter + value;
+                }
+                if (index == -1) {
+                    mLines.add(key);
+                } else {
+                    mLines.set(index, key);
+                }
+            } else if (index != -1) {
+                mLines.remove(index);
+            }
+        }
+
+        /**
+         * Returns the value of the key.
+         */
+        private String get(String key, char delimiter) {
+            int index = find(key, delimiter);
+            if (index == -1) {
+                return null;
+            }
+            String line = mLines.get(index);
+            int length = key.length();
+            return (line.length() == length) ? "" : line.substring(length + 1);
+        }
+    }
+}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 0069fe0..c23da20 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -16,120 +16,184 @@
 
 package android.net.sip;
 
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.net.rtp.AudioCodec;
 import android.net.rtp.AudioGroup;
 import android.net.rtp.AudioStream;
+import android.net.rtp.RtpStream;
+import android.net.sip.SimpleSessionDescription.Media;
+import android.net.wifi.WifiManager;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
- * Interface for making audio calls over SIP.
- * @hide
+ * Class that handles an audio call over SIP.
  */
-public interface SipAudioCall {
+/** @hide */
+public class SipAudioCall {
+    private static final String TAG = SipAudioCall.class.getSimpleName();
+    private static final boolean RELEASE_SOCKET = true;
+    private static final boolean DONT_RELEASE_SOCKET = false;
+    private static final int SESSION_TIMEOUT = 5; // in seconds
+
     /** Listener class for all event callbacks. */
-    public interface Listener {
+    public static class Listener {
         /**
          * Called when the call object is ready to make another call.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that is ready to make another call
          */
-        void onReadyToCall(SipAudioCall call);
+        public void onReadyToCall(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when a request is sent out to initiate a new call.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCalling(SipAudioCall call);
+        public void onCalling(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when a new call comes in.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          * @param caller the SIP profile of the caller
          */
-        void onRinging(SipAudioCall call, SipProfile caller);
+        public void onRinging(SipAudioCall call, SipProfile caller) {
+            onChanged(call);
+        }
 
         /**
-         * Called when a RINGING response is received for the INVITE request sent
+         * Called when a RINGING response is received for the INVITE request
+         * sent. The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onRingingBack(SipAudioCall call);
+        public void onRingingBack(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the session is established.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallEstablished(SipAudioCall call);
+        public void onCallEstablished(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the session is terminated.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallEnded(SipAudioCall call);
+        public void onCallEnded(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the peer is busy during session initialization.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallBusy(SipAudioCall call);
+        public void onCallBusy(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the call is on hold.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallHeld(SipAudioCall call);
+        public void onCallHeld(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
-         * Called when an error occurs.
+         * Called when an error occurs. The default implementation is no op.
          *
          * @param call the call object that carries out the audio call
          * @param errorCode error code of this error
          * @param errorMessage error message
          * @see SipErrorCode
          */
-        void onError(SipAudioCall call, int errorCode, String errorMessage);
-    }
-
-    /**
-     * The adapter class for {@link Listener}. The default implementation of
-     * all callback methods is no-op.
-     */
-    public class Adapter implements Listener {
-        protected void onChanged(SipAudioCall call) {
-        }
-        public void onReadyToCall(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCalling(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onRinging(SipAudioCall call, SipProfile caller) {
-            onChanged(call);
-        }
-        public void onRingingBack(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallEstablished(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallEnded(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallBusy(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallHeld(SipAudioCall call) {
-            onChanged(call);
-        }
         public void onError(SipAudioCall call, int errorCode,
                 String errorMessage) {
-            onChanged(call);
+            // no-op
         }
+
+        /**
+         * Called when an event occurs and the corresponding callback is not
+         * overridden. The default implementation is no op. Error events are
+         * not re-directed to this callback and are handled in {@link #onError}.
+         */
+        public void onChanged(SipAudioCall call) {
+            // no-op
+        }
+    }
+
+    private Context mContext;
+    private SipProfile mLocalProfile;
+    private SipAudioCall.Listener mListener;
+    private SipSession mSipSession;
+
+    private long mSessionId = System.currentTimeMillis();
+    private String mPeerSd;
+
+    private AudioStream mAudioStream;
+    private AudioGroup mAudioGroup;
+
+    private boolean mInCall = false;
+    private boolean mMuted = false;
+    private boolean mHold = false;
+
+    private boolean mRingbackToneEnabled = true;
+    private boolean mRingtoneEnabled = true;
+    private Ringtone mRingtone;
+    private ToneGenerator mRingbackTone;
+
+    private SipProfile mPendingCallRequest;
+    private WifiManager mWm;
+    private WifiManager.WifiLock mWifiHighPerfLock;
+
+    private int mErrorCode = SipErrorCode.NO_ERROR;
+    private String mErrorMessage;
+
+    /**
+     * Creates a call object with the local SIP profile.
+     * @param context the context for accessing system services such as
+     *        ringtone, audio, WIFI etc
+     */
+    public SipAudioCall(Context context, SipProfile localProfile) {
+        mContext = context;
+        mLocalProfile = localProfile;
+        mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
     }
 
     /**
@@ -139,7 +203,9 @@
      * @param listener to listen to the audio call events of this object
      * @see #setListener(Listener, boolean)
      */
-    void setListener(Listener listener);
+    public void setListener(SipAudioCall.Listener listener) {
+        setListener(listener, false);
+    }
 
     /**
      * Sets the listener to listen to the audio call events. A
@@ -150,12 +216,312 @@
      * @param callbackImmediately set to true if the caller wants to be called
      *      back immediately on the current state
      */
-    void setListener(Listener listener, boolean callbackImmediately);
+    public void setListener(SipAudioCall.Listener listener,
+            boolean callbackImmediately) {
+        mListener = listener;
+        try {
+            if ((listener == null) || !callbackImmediately) {
+                // do nothing
+            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
+                listener.onError(this, mErrorCode, mErrorMessage);
+            } else if (mInCall) {
+                if (mHold) {
+                    listener.onCallHeld(this);
+                } else {
+                    listener.onCallEstablished(this);
+                }
+            } else {
+                int state = getState();
+                switch (state) {
+                    case SipSession.State.READY_TO_CALL:
+                        listener.onReadyToCall(this);
+                        break;
+                    case SipSession.State.INCOMING_CALL:
+                        listener.onRinging(this, getPeerProfile());
+                        break;
+                    case SipSession.State.OUTGOING_CALL:
+                        listener.onCalling(this);
+                        break;
+                    case SipSession.State.OUTGOING_CALL_RING_BACK:
+                        listener.onRingingBack(this);
+                        break;
+                }
+            }
+        } catch (Throwable t) {
+            Log.e(TAG, "setListener()", t);
+        }
+    }
+
+    /**
+     * Checks if the call is established.
+     *
+     * @return true if the call is established
+     */
+    public synchronized boolean isInCall() {
+        return mInCall;
+    }
+
+    /**
+     * Checks if the call is on hold.
+     *
+     * @return true if the call is on hold
+     */
+    public synchronized boolean isOnHold() {
+        return mHold;
+    }
 
     /**
      * Closes this object. This object is not usable after being closed.
      */
-    void close();
+    public void close() {
+        close(true);
+    }
+
+    private synchronized void close(boolean closeRtp) {
+        if (closeRtp) stopCall(RELEASE_SOCKET);
+        stopRingbackTone();
+        stopRinging();
+
+        mInCall = false;
+        mHold = false;
+        mSessionId = System.currentTimeMillis();
+        mErrorCode = SipErrorCode.NO_ERROR;
+        mErrorMessage = null;
+
+        if (mSipSession != null) {
+            mSipSession.setListener(null);
+            mSipSession = null;
+        }
+    }
+
+    /**
+     * Gets the local SIP profile.
+     *
+     * @return the local SIP profile
+     */
+    public synchronized SipProfile getLocalProfile() {
+        return mLocalProfile;
+    }
+
+    /**
+     * Gets the peer's SIP profile.
+     *
+     * @return the peer's SIP profile
+     */
+    public synchronized SipProfile getPeerProfile() {
+        return (mSipSession == null) ? null : mSipSession.getPeerProfile();
+    }
+
+    /**
+     * Gets the state of the {@link SipSession} that carries this call.
+     * The value returned must be one of the states in {@link SipSession.State}.
+     *
+     * @return the session state
+     */
+    public synchronized int getState() {
+        if (mSipSession == null) return SipSession.State.READY_TO_CALL;
+        return mSipSession.getState();
+    }
+
+
+    /**
+     * Gets the {@link SipSession} that carries this call.
+     *
+     * @return the session object that carries this call
+     * @hide
+     */
+    public synchronized SipSession getSipSession() {
+        return mSipSession;
+    }
+
+    private SipSession.Listener createListener() {
+        return new SipSession.Listener() {
+            @Override
+            public void onCalling(SipSession session) {
+                Log.d(TAG, "calling... " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCalling(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCalling(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onRingingBack(SipSession session) {
+                Log.d(TAG, "sip call ringing back: " + session);
+                if (!mInCall) startRingbackTone();
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onRingingBack(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onRingingBack(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public synchronized void onRinging(SipSession session,
+                    SipProfile peerProfile, String sessionDescription) {
+                if ((mSipSession == null) || !mInCall
+                        || !session.getCallId().equals(mSipSession.getCallId())) {
+                    // should not happen
+                    session.endCall();
+                    return;
+                }
+
+                // session changing request
+                try {
+                    String answer = createAnswer(sessionDescription).encode();
+                    mSipSession.answerCall(answer, SESSION_TIMEOUT);
+                } catch (Throwable e) {
+                    Log.e(TAG, "onRinging()", e);
+                    session.endCall();
+                }
+            }
+
+            @Override
+            public void onCallEstablished(SipSession session,
+                    String sessionDescription) {
+                stopRingbackTone();
+                stopRinging();
+                mPeerSd = sessionDescription;
+                Log.v(TAG, "onCallEstablished()" + mPeerSd);
+
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        if (mHold) {
+                            listener.onCallHeld(SipAudioCall.this);
+                        } else {
+                            listener.onCallEstablished(SipAudioCall.this);
+                        }
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallEstablished(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onCallEnded(SipSession session) {
+                Log.d(TAG, "sip call ended: " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCallEnded(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallEnded(): " + t);
+                    }
+                }
+                close();
+            }
+
+            @Override
+            public void onCallBusy(SipSession session) {
+                Log.d(TAG, "sip call busy: " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCallBusy(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallBusy(): " + t);
+                    }
+                }
+                close(false);
+            }
+
+            @Override
+            public void onCallChangeFailed(SipSession session, int errorCode,
+                    String message) {
+                Log.d(TAG, "sip call change failed: " + message);
+                mErrorCode = errorCode;
+                mErrorMessage = message;
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onError(SipAudioCall.this, mErrorCode,
+                                message);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallBusy(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onError(SipSession session, int errorCode,
+                    String message) {
+                SipAudioCall.this.onError(errorCode, message);
+            }
+
+            @Override
+            public void onRegistering(SipSession session) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationTimeout(SipSession session) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationFailed(SipSession session, int errorCode,
+                    String message) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationDone(SipSession session, int duration) {
+                // irrelevant
+            }
+        };
+    }
+
+    private void onError(int errorCode, String message) {
+        Log.d(TAG, "sip session error: "
+                + SipErrorCode.toString(errorCode) + ": " + message);
+        mErrorCode = errorCode;
+        mErrorMessage = message;
+        Listener listener = mListener;
+        if (listener != null) {
+            try {
+                listener.onError(this, errorCode, message);
+            } catch (Throwable t) {
+                Log.i(TAG, "onError(): " + t);
+            }
+        }
+        synchronized (this) {
+            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
+                    || !isInCall()) {
+                close(true);
+            }
+        }
+    }
+
+    /**
+     * Attaches an incoming call to this call object.
+     *
+     * @param session the session that receives the incoming call
+     * @param sessionDescription the session description of the incoming call
+     * @throws SipException if the SIP service fails to attach this object to
+     *        the session
+     */
+    public synchronized void attachCall(SipSession session,
+            String sessionDescription) throws SipException {
+        mSipSession = session;
+        mPeerSd = sessionDescription;
+        Log.v(TAG, "attachCall()" + mPeerSd);
+        try {
+            session.setListener(createListener());
+
+            if (getState() == SipSession.State.INCOMING_CALL) startRinging();
+        } catch (Throwable e) {
+            Log.e(TAG, "attachCall()", e);
+            throwSipException(e);
+        }
+    }
 
     /**
      * Initiates an audio call to the specified profile. The attempt will be
@@ -164,30 +530,37 @@
      * will be called.
      *
      * @param callee the SIP profile to make the call to
-     * @param sipManager the {@link SipManager} object to help make call with
-     * @param timeout the timeout value in seconds
+     * @param sipSession the {@link SipSession} for carrying out the call
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to create a session for the
+     *        call
      */
-    void makeCall(SipProfile callee, SipManager sipManager, int timeout)
-            throws SipException;
+    public synchronized void makeCall(SipProfile peerProfile,
+            SipSession sipSession, int timeout) throws SipException {
+        mSipSession = sipSession;
+        try {
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            sipSession.setListener(createListener());
+            sipSession.makeCall(peerProfile, createOffer().encode(), timeout);
+        } catch (IOException e) {
+            throw new SipException("makeCall()", e);
+        }
+    }
 
     /**
-     * Starts the audio for the established call. This method should be called
-     * after {@link Listener#onCallEstablished} is called.
+     * Ends a call.
+     * @throws SipException if the SIP service fails to end the call
      */
-    void startAudio();
+    public synchronized void endCall() throws SipException {
+        stopRinging();
+        stopCall(RELEASE_SOCKET);
+        mInCall = false;
 
-    /**
-     * Attaches an incoming call to this call object.
-     *
-     * @param session the session that receives the incoming call
-     * @param sessionDescription the session description of the incoming call
-     */
-    void attachCall(ISipSession session, String sessionDescription)
-            throws SipException;
-
-    /** Ends a call. */
-    void endCall() throws SipException;
+        // perform the above local ops first and then network op
+        if (mSipSession != null) mSipSession.endCall();
+    }
 
     /**
      * Puts a call on hold.  When succeeds, {@link Listener#onCallHeld} is
@@ -196,10 +569,19 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to hold the call
      */
-    void holdCall(int timeout) throws SipException;
+    public synchronized void holdCall(int timeout) throws SipException {
+        if (mHold) return;
+        mSipSession.changeCall(createHoldOffer().encode(), timeout);
+        mHold = true;
+
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+    }
 
     /**
      * Answers a call. The attempt will be timed out if the call is not
@@ -207,10 +589,20 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to answer the call
      */
-    void answerCall(int timeout) throws SipException;
+    public synchronized void answerCall(int timeout) throws SipException {
+        stopRinging();
+        try {
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
+        } catch (IOException e) {
+            throw new SipException("answerCall()", e);
+        }
+    }
 
     /**
      * Continues a call that's on hold. When succeeds,
@@ -219,45 +611,189 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to unhold the call
      */
-    void continueCall(int timeout) throws SipException;
+    public synchronized void continueCall(int timeout) throws SipException {
+        if (!mHold) return;
+        mSipSession.changeCall(createContinueOffer().encode(), timeout);
+        mHold = false;
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
+    }
 
-    /** Puts the device to speaker mode. */
-    void setSpeakerMode(boolean speakerMode);
+    private SimpleSessionDescription createOffer() {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        AudioCodec[] codecs = AudioCodec.getCodecs();
+        Media media = offer.newMedia(
+                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+        for (AudioCodec codec : AudioCodec.getCodecs()) {
+            media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
+        }
+        media.setRtpPayload(127, "telephone-event/8000", "0-15");
+        return offer;
+    }
+
+    private SimpleSessionDescription createAnswer(String offerSd) {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(offerSd);
+        SimpleSessionDescription answer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        AudioCodec codec = null;
+        for (Media media : offer.getMedia()) {
+            if ((codec == null) && (media.getPort() > 0)
+                    && "audio".equals(media.getType())
+                    && "RTP/AVP".equals(media.getProtocol())) {
+                // Find the first audio codec we supported.
+                for (int type : media.getRtpPayloadTypes()) {
+                    codec = AudioCodec.getCodec(type, media.getRtpmap(type),
+                            media.getFmtp(type));
+                    if (codec != null) {
+                        break;
+                    }
+                }
+                if (codec != null) {
+                    Media reply = answer.newMedia(
+                            "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+                    reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
+
+                    // Check if DTMF is supported in the same media.
+                    for (int type : media.getRtpPayloadTypes()) {
+                        String rtpmap = media.getRtpmap(type);
+                        if ((type != codec.type) && (rtpmap != null)
+                                && rtpmap.startsWith("telephone-event")) {
+                            reply.setRtpPayload(
+                                    type, rtpmap, media.getFmtp(type));
+                        }
+                    }
+
+                    // Handle recvonly and sendonly.
+                    if (media.getAttribute("recvonly") != null) {
+                        answer.setAttribute("sendonly", "");
+                    } else if(media.getAttribute("sendonly") != null) {
+                        answer.setAttribute("recvonly", "");
+                    } else if(offer.getAttribute("recvonly") != null) {
+                        answer.setAttribute("sendonly", "");
+                    } else if(offer.getAttribute("sendonly") != null) {
+                        answer.setAttribute("recvonly", "");
+                    }
+                    continue;
+                }
+            }
+            // Reject the media.
+            Media reply = answer.newMedia(
+                    media.getType(), 0, 1, media.getProtocol());
+            for (String format : media.getFormats()) {
+                reply.setFormat(format, null);
+            }
+        }
+        if (codec == null) {
+            throw new IllegalStateException("Reject SDP: no suitable codecs");
+        }
+        return answer;
+    }
+
+    private SimpleSessionDescription createHoldOffer() {
+        SimpleSessionDescription offer = createContinueOffer();
+        offer.setAttribute("sendonly", "");
+        return offer;
+    }
+
+    private SimpleSessionDescription createContinueOffer() {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        Media media = offer.newMedia(
+                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+        AudioCodec codec = mAudioStream.getCodec();
+        media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
+        int dtmfType = mAudioStream.getDtmfType();
+        if (dtmfType != -1) {
+            media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15");
+        }
+        return offer;
+    }
+
+    private void grabWifiHighPerfLock() {
+        if (mWifiHighPerfLock == null) {
+            Log.v(TAG, "acquire wifi high perf lock");
+            mWifiHighPerfLock = ((WifiManager)
+                    mContext.getSystemService(Context.WIFI_SERVICE))
+                    .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
+            mWifiHighPerfLock.acquire();
+        }
+    }
+
+    private void releaseWifiHighPerfLock() {
+        if (mWifiHighPerfLock != null) {
+            Log.v(TAG, "release wifi high perf lock");
+            mWifiHighPerfLock.release();
+            mWifiHighPerfLock = null;
+        }
+    }
+
+    private boolean isWifiOn() {
+        return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
+    }
 
     /** Toggles mute. */
-    void toggleMute();
-
-    /**
-     * Checks if the call is on hold.
-     *
-     * @return true if the call is on hold
-     */
-    boolean isOnHold();
+    public synchronized void toggleMute() {
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) {
+            audioGroup.setMode(
+                    mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED);
+            mMuted = !mMuted;
+        }
+    }
 
     /**
      * Checks if the call is muted.
      *
      * @return true if the call is muted
      */
-    boolean isMuted();
+    public synchronized boolean isMuted() {
+        return mMuted;
+    }
+
+    /** Puts the device to speaker mode. */
+    public synchronized void setSpeakerMode(boolean speakerMode) {
+        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
+                .setSpeakerphoneOn(speakerMode);
+    }
 
     /**
-     * Sends a DTMF code.
+     * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
+     * flash to 16. Currently, event flash is not supported.
      *
-     * @param code the DTMF code to send
+     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
+     *        inputs.
+     * @see http://tools.ietf.org/html/rfc2833
      */
-    void sendDtmf(int code);
+    public void sendDtmf(int code) {
+        sendDtmf(code, null);
+    }
 
     /**
-     * Sends a DTMF code.
+     * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
+     * flash to 16. Currently, event flash is not supported.
      *
-     * @param code the DTMF code to send
+     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
+     *        inputs.
      * @param result the result message to send when done
      */
-    void sendDtmf(int code, Message result);
+    public synchronized void sendDtmf(int code, Message result) {
+        AudioGroup audioGroup = getAudioGroup();
+        if ((audioGroup != null) && (mSipSession != null)
+                && (SipSession.State.IN_CALL == getState())) {
+            Log.v(TAG, "send DTMF: " + code);
+            audioGroup.sendDtmf(code);
+        }
+        if (result != null) result.sendToTarget();
+    }
 
     /**
      * Gets the {@link AudioStream} object used in this call. The object
@@ -268,8 +804,11 @@
      *
      * @return the {@link AudioStream} object or null if the RTP stream has not
      *      yet been set up
+     * @hide
      */
-    AudioStream getAudioStream();
+    public synchronized AudioStream getAudioStream() {
+        return mAudioStream;
+    }
 
     /**
      * Gets the {@link AudioGroup} object which the {@link AudioStream} object
@@ -283,8 +822,12 @@
      * @return the {@link AudioGroup} object or null if the RTP stream has not
      *      yet been set up
      * @see #getAudioStream
+     * @hide
      */
-    AudioGroup getAudioGroup();
+    public synchronized AudioGroup getAudioGroup() {
+        if (mAudioGroup != null) return mAudioGroup;
+        return ((mAudioStream == null) ? null : mAudioStream.getGroup());
+    }
 
     /**
      * Sets the {@link AudioGroup} object which the {@link AudioStream} object
@@ -292,56 +835,214 @@
      * will be dynamically created when needed.
      *
      * @see #getAudioStream
+     * @hide
      */
-    void setAudioGroup(AudioGroup audioGroup);
+    public synchronized void setAudioGroup(AudioGroup group) {
+        if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
+            mAudioStream.join(group);
+        }
+        mAudioGroup = group;
+    }
 
     /**
-     * Checks if the call is established.
-     *
-     * @return true if the call is established
+     * Starts the audio for the established call. This method should be called
+     * after {@link Listener#onCallEstablished} is called.
      */
-    boolean isInCall();
+    public void startAudio() {
+        try {
+            startAudioInternal();
+        } catch (UnknownHostException e) {
+            onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage());
+        } catch (Throwable e) {
+            onError(SipErrorCode.CLIENT_ERROR, e.getMessage());
+        }
+    }
 
-    /**
-     * Gets the local SIP profile.
-     *
-     * @return the local SIP profile
-     */
-    SipProfile getLocalProfile();
+    private synchronized void startAudioInternal() throws UnknownHostException {
+        if (mPeerSd == null) {
+            Log.v(TAG, "startAudioInternal() mPeerSd = null");
+            throw new IllegalStateException("mPeerSd = null");
+        }
 
-    /**
-     * Gets the peer's SIP profile.
-     *
-     * @return the peer's SIP profile
-     */
-    SipProfile getPeerProfile();
+        stopCall(DONT_RELEASE_SOCKET);
+        mInCall = true;
 
-    /**
-     * Gets the state of the {@link ISipSession} that carries this call.
-     * The value returned must be one of the states in {@link SipSessionState}.
-     *
-     * @return the session state
-     */
-    int getState();
+        // Run exact the same logic in createAnswer() to setup mAudioStream.
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mPeerSd);
+        AudioStream stream = mAudioStream;
+        AudioCodec codec = null;
+        for (Media media : offer.getMedia()) {
+            if ((codec == null) && (media.getPort() > 0)
+                    && "audio".equals(media.getType())
+                    && "RTP/AVP".equals(media.getProtocol())) {
+                // Find the first audio codec we supported.
+                for (int type : media.getRtpPayloadTypes()) {
+                    codec = AudioCodec.getCodec(
+                            type, media.getRtpmap(type), media.getFmtp(type));
+                    if (codec != null) {
+                        break;
+                    }
+                }
 
-    /**
-     * Gets the {@link ISipSession} that carries this call.
-     *
-     * @return the session object that carries this call
-     */
-    ISipSession getSipSession();
+                if (codec != null) {
+                    // Associate with the remote host.
+                    String address = media.getAddress();
+                    if (address == null) {
+                        address = offer.getAddress();
+                    }
+                    stream.associate(InetAddress.getByName(address),
+                            media.getPort());
+
+                    stream.setDtmfType(-1);
+                    stream.setCodec(codec);
+                    // Check if DTMF is supported in the same media.
+                    for (int type : media.getRtpPayloadTypes()) {
+                        String rtpmap = media.getRtpmap(type);
+                        if ((type != codec.type) && (rtpmap != null)
+                                && rtpmap.startsWith("telephone-event")) {
+                            stream.setDtmfType(type);
+                        }
+                    }
+
+                    // Handle recvonly and sendonly.
+                    if (mHold) {
+                        stream.setMode(RtpStream.MODE_NORMAL);
+                    } else if (media.getAttribute("recvonly") != null) {
+                        stream.setMode(RtpStream.MODE_SEND_ONLY);
+                    } else if(media.getAttribute("sendonly") != null) {
+                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
+                    } else if(offer.getAttribute("recvonly") != null) {
+                        stream.setMode(RtpStream.MODE_SEND_ONLY);
+                    } else if(offer.getAttribute("sendonly") != null) {
+                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
+                    } else {
+                        stream.setMode(RtpStream.MODE_NORMAL);
+                    }
+                    break;
+                }
+            }
+        }
+        if (codec == null) {
+            throw new IllegalStateException("Reject SDP: no suitable codecs");
+        }
+
+        if (isWifiOn()) grabWifiHighPerfLock();
+
+        if (!mHold) {
+            /* The recorder volume will be very low if the device is in
+             * IN_CALL mode. Therefore, we have to set the mode to NORMAL
+             * in order to have the normal microphone level.
+             */
+            ((AudioManager) mContext.getSystemService
+                    (Context.AUDIO_SERVICE))
+                    .setMode(AudioManager.MODE_NORMAL);
+        }
+
+        // AudioGroup logic:
+        AudioGroup audioGroup = getAudioGroup();
+        if (mHold) {
+            if (audioGroup != null) {
+                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+            }
+            // don't create an AudioGroup here; doing so will fail if
+            // there's another AudioGroup out there that's active
+        } else {
+            if (audioGroup == null) audioGroup = new AudioGroup();
+            stream.join(audioGroup);
+            if (mMuted) {
+                audioGroup.setMode(AudioGroup.MODE_MUTED);
+            } else {
+                audioGroup.setMode(AudioGroup.MODE_NORMAL);
+            }
+        }
+    }
+
+    private void stopCall(boolean releaseSocket) {
+        Log.d(TAG, "stop audiocall");
+        releaseWifiHighPerfLock();
+        if (mAudioStream != null) {
+            mAudioStream.join(null);
+
+            if (releaseSocket) {
+                mAudioStream.release();
+                mAudioStream = null;
+            }
+        }
+    }
+
+    private String getLocalIp() {
+        return mSipSession.getLocalIp();
+    }
+
 
     /**
      * Enables/disables the ring-back tone.
      *
      * @param enabled true to enable; false to disable
      */
-    void setRingbackToneEnabled(boolean enabled);
+    public synchronized void setRingbackToneEnabled(boolean enabled) {
+        mRingbackToneEnabled = enabled;
+    }
 
     /**
      * Enables/disables the ring tone.
      *
      * @param enabled true to enable; false to disable
      */
-    void setRingtoneEnabled(boolean enabled);
+    public synchronized void setRingtoneEnabled(boolean enabled) {
+        mRingtoneEnabled = enabled;
+    }
+
+    private void startRingbackTone() {
+        if (!mRingbackToneEnabled) return;
+        if (mRingbackTone == null) {
+            // The volume relative to other sounds in the stream
+            int toneVolume = 80;
+            mRingbackTone = new ToneGenerator(
+                    AudioManager.STREAM_VOICE_CALL, toneVolume);
+        }
+        mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
+    }
+
+    private void stopRingbackTone() {
+        if (mRingbackTone != null) {
+            mRingbackTone.stopTone();
+            mRingbackTone.release();
+            mRingbackTone = null;
+        }
+    }
+
+    private void startRinging() {
+        if (!mRingtoneEnabled) return;
+        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
+                .vibrate(new long[] {0, 1000, 1000}, 1);
+        AudioManager am = (AudioManager)
+                mContext.getSystemService(Context.AUDIO_SERVICE);
+        if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
+            String ringtoneUri =
+                    Settings.System.DEFAULT_RINGTONE_URI.toString();
+            mRingtone = RingtoneManager.getRingtone(mContext,
+                    Uri.parse(ringtoneUri));
+            mRingtone.play();
+        }
+    }
+
+    private void stopRinging() {
+        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
+                .cancel();
+        if (mRingtone != null) mRingtone.stop();
+    }
+
+    private void throwSipException(Throwable throwable) throws SipException {
+        if (throwable instanceof SipException) {
+            throw (SipException) throwable;
+        } else {
+            throw new SipException("", throwable);
+        }
+    }
+
+    private SipProfile getPeerProfile(SipSession session) {
+        return session.getPeerProfile();
+    }
 }
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
deleted file mode 100644
index ccf4d15..0000000
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ /dev/null
@@ -1,777 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import gov.nist.javax.sdp.fields.SDPKeywords;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
-import android.net.rtp.AudioCodec;
-import android.net.rtp.AudioGroup;
-import android.net.rtp.AudioStream;
-import android.net.rtp.RtpStream;
-import android.net.wifi.WifiManager;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import javax.sdp.SdpException;
-
-/**
- * Class that handles an audio call over SIP. 
- */
-/** @hide */
-public class SipAudioCallImpl extends SipSessionAdapter
-        implements SipAudioCall {
-    private static final String TAG = SipAudioCallImpl.class.getSimpleName();
-    private static final boolean RELEASE_SOCKET = true;
-    private static final boolean DONT_RELEASE_SOCKET = false;
-    private static final String AUDIO = "audio";
-    private static final int DTMF = 101;
-    private static final int SESSION_TIMEOUT = 5; // in seconds
-
-    private Context mContext;
-    private SipProfile mLocalProfile;
-    private SipAudioCall.Listener mListener;
-    private ISipSession mSipSession;
-    private SdpSessionDescription mPeerSd;
-
-    private AudioStream mAudioStream;
-    private AudioGroup mAudioGroup;
-    private SdpSessionDescription.AudioCodec mCodec;
-    private long mSessionId = -1L; // SDP session ID
-    private boolean mInCall = false;
-    private boolean mMuted = false;
-    private boolean mHold = false;
-
-    private boolean mRingbackToneEnabled = true;
-    private boolean mRingtoneEnabled = true;
-    private Ringtone mRingtone;
-    private ToneGenerator mRingbackTone;
-
-    private SipProfile mPendingCallRequest;
-    private WifiManager mWm;
-    private WifiManager.WifiLock mWifiHighPerfLock;
-
-    private int mErrorCode = SipErrorCode.NO_ERROR;
-    private String mErrorMessage;
-
-    public SipAudioCallImpl(Context context, SipProfile localProfile) {
-        mContext = context;
-        mLocalProfile = localProfile;
-        mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-    }
-
-    public void setListener(SipAudioCall.Listener listener) {
-        setListener(listener, false);
-    }
-
-    public void setListener(SipAudioCall.Listener listener,
-            boolean callbackImmediately) {
-        mListener = listener;
-        try {
-            if ((listener == null) || !callbackImmediately) {
-                // do nothing
-            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
-                listener.onError(this, mErrorCode, mErrorMessage);
-            } else if (mInCall) {
-                if (mHold) {
-                    listener.onCallHeld(this);
-                } else {
-                    listener.onCallEstablished(this);
-                }
-            } else {
-                int state = getState();
-                switch (state) {
-                    case SipSessionState.READY_TO_CALL:
-                        listener.onReadyToCall(this);
-                        break;
-                    case SipSessionState.INCOMING_CALL:
-                        listener.onRinging(this, getPeerProfile(mSipSession));
-                        break;
-                    case SipSessionState.OUTGOING_CALL:
-                        listener.onCalling(this);
-                        break;
-                    case SipSessionState.OUTGOING_CALL_RING_BACK:
-                        listener.onRingingBack(this);
-                        break;
-                }
-            }
-        } catch (Throwable t) {
-            Log.e(TAG, "setListener()", t);
-        }
-    }
-
-    public synchronized boolean isInCall() {
-        return mInCall;
-    }
-
-    public synchronized boolean isOnHold() {
-        return mHold;
-    }
-
-    public void close() {
-        close(true);
-    }
-
-    private synchronized void close(boolean closeRtp) {
-        if (closeRtp) stopCall(RELEASE_SOCKET);
-        stopRingbackTone();
-        stopRinging();
-
-        mInCall = false;
-        mHold = false;
-        mSessionId = -1L;
-        mErrorCode = SipErrorCode.NO_ERROR;
-        mErrorMessage = null;
-
-        if (mSipSession != null) {
-            try {
-                mSipSession.setListener(null);
-            } catch (RemoteException e) {
-                // don't care
-            }
-            mSipSession = null;
-        }
-    }
-
-    public synchronized SipProfile getLocalProfile() {
-        return mLocalProfile;
-    }
-
-    public synchronized SipProfile getPeerProfile() {
-        try {
-            return (mSipSession == null) ? null : mSipSession.getPeerProfile();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    public synchronized int getState() {
-        if (mSipSession == null) return SipSessionState.READY_TO_CALL;
-        try {
-            return mSipSession.getState();
-        } catch (RemoteException e) {
-            return SipSessionState.REMOTE_ERROR;
-        }
-    }
-
-
-    public synchronized ISipSession getSipSession() {
-        return mSipSession;
-    }
-
-    @Override
-    public void onCalling(ISipSession session) {
-        Log.d(TAG, "calling... " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCalling(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCalling()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onRingingBack(ISipSession session) {
-        Log.d(TAG, "sip call ringing back: " + session);
-        if (!mInCall) startRingbackTone();
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onRingingBack(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onRingingBack()", t);
-            }
-        }
-    }
-
-    @Override
-    public synchronized void onRinging(ISipSession session,
-            SipProfile peerProfile, String sessionDescription) {
-        try {
-            if ((mSipSession == null) || !mInCall
-                    || !session.getCallId().equals(mSipSession.getCallId())) {
-                // should not happen
-                session.endCall();
-                return;
-            }
-
-            // session changing request
-            try {
-                mPeerSd = new SdpSessionDescription(sessionDescription);
-                answerCall(SESSION_TIMEOUT);
-            } catch (Throwable e) {
-                Log.e(TAG, "onRinging()", e);
-                session.endCall();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "onRinging()", e);
-        }
-    }
-
-    @Override
-    public void onCallEstablished(ISipSession session,
-            String sessionDescription) {
-        stopRingbackTone();
-        stopRinging();
-        try {
-            mPeerSd = new SdpSessionDescription(sessionDescription);
-            Log.d(TAG, "sip call established: " + mPeerSd);
-        } catch (SdpException e) {
-            Log.e(TAG, "createSessionDescription()", e);
-        }
-
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                if (mHold) {
-                    listener.onCallHeld(this);
-                } else {
-                    listener.onCallEstablished(this);
-                }
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallEstablished()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onCallEnded(ISipSession session) {
-        Log.d(TAG, "sip call ended: " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCallEnded(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallEnded()", t);
-            }
-        }
-        close();
-    }
-
-    @Override
-    public void onCallBusy(ISipSession session) {
-        Log.d(TAG, "sip call busy: " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCallBusy(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallBusy()", t);
-            }
-        }
-        close(false);
-    }
-
-    @Override
-    public void onCallChangeFailed(ISipSession session, int errorCode,
-            String message) {
-        Log.d(TAG, "sip call change failed: " + message);
-        mErrorCode = errorCode;
-        mErrorMessage = message;
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onError(this, mErrorCode, message);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallBusy()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onError(ISipSession session, int errorCode, String message) {
-        Log.d(TAG, "sip session error: " + SipErrorCode.toString(errorCode)
-                + ": " + message);
-        mErrorCode = errorCode;
-        mErrorMessage = message;
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onError(this, errorCode, message);
-            } catch (Throwable t) {
-                Log.e(TAG, "onError()", t);
-            }
-        }
-        synchronized (this) {
-            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
-                    || !isInCall()) {
-                close(true);
-            }
-        }
-    }
-
-    public synchronized void attachCall(ISipSession session,
-            String sessionDescription) throws SipException {
-        mSipSession = session;
-        try {
-            mPeerSd = new SdpSessionDescription(sessionDescription);
-            session.setListener(this);
-
-            if (getState() == SipSessionState.INCOMING_CALL) startRinging();
-        } catch (Throwable e) {
-            Log.e(TAG, "attachCall()", e);
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void makeCall(SipProfile peerProfile,
-            SipManager sipManager, int timeout) throws SipException {
-        try {
-            mSipSession = sipManager.createSipSession(mLocalProfile, this);
-            if (mSipSession == null) {
-                throw new SipException(
-                        "Failed to create SipSession; network available?");
-            }
-            mSipSession.makeCall(peerProfile, createOfferSessionDescription(),
-                    timeout);
-        } catch (Throwable e) {
-            if (e instanceof SipException) {
-                throw (SipException) e;
-            } else {
-                throwSipException(e);
-            }
-        }
-    }
-
-    public synchronized void endCall() throws SipException {
-        try {
-            stopRinging();
-            stopCall(true);
-            mInCall = false;
-
-            // perform the above local ops first and then network op
-            if (mSipSession != null) mSipSession.endCall();
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void holdCall(int timeout) throws SipException {
-        if (mHold) return;
-        try {
-            mSipSession.changeCall(createHoldSessionDescription(), timeout);
-            mHold = true;
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-    }
-
-    public synchronized void answerCall(int timeout) throws SipException {
-        try {
-            stopRinging();
-            mSipSession.answerCall(createAnswerSessionDescription(), timeout);
-        } catch (Throwable e) {
-            Log.e(TAG, "answerCall()", e);
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void continueCall(int timeout) throws SipException {
-        if (!mHold) return;
-        try {
-            mHold = false;
-            mSipSession.changeCall(createContinueSessionDescription(), timeout);
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
-    }
-
-    private String createOfferSessionDescription() {
-        AudioCodec[] codecs = AudioCodec.getSystemSupportedCodecs();
-        return createSdpBuilder(true, convert(codecs)).build();
-    }
-
-    private String createAnswerSessionDescription() {
-        try {
-            // choose an acceptable media from mPeerSd to answer
-            SdpSessionDescription.AudioCodec codec = getCodec(mPeerSd);
-            SdpSessionDescription.Builder sdpBuilder =
-                    createSdpBuilder(false, codec);
-            if (mPeerSd.isSendOnly(AUDIO)) {
-                sdpBuilder.addMediaAttribute(AUDIO, "recvonly", (String) null);
-            } else if (mPeerSd.isReceiveOnly(AUDIO)) {
-                sdpBuilder.addMediaAttribute(AUDIO, "sendonly", (String) null);
-            }
-            return sdpBuilder.build();
-        } catch (SdpException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private String createHoldSessionDescription() {
-        try {
-            return createSdpBuilder(false, mCodec)
-                    .addMediaAttribute(AUDIO, "sendonly", (String) null)
-                    .build();
-        } catch (SdpException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private void grabWifiHighPerfLock() {
-        if (mWifiHighPerfLock == null) {
-            Log.v(TAG, "acquire wifi high perf lock");
-            mWifiHighPerfLock = ((WifiManager)
-                    mContext.getSystemService(Context.WIFI_SERVICE))
-                    .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
-            mWifiHighPerfLock.acquire();
-        }
-    }
-
-    private void releaseWifiHighPerfLock() {
-        if (mWifiHighPerfLock != null) {
-            Log.v(TAG, "release wifi high perf lock");
-            mWifiHighPerfLock.release();
-            mWifiHighPerfLock = null;
-        }
-    }
-
-    private boolean isWifiOn() {
-        return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
-    }
-
-    private String createContinueSessionDescription() {
-        return createSdpBuilder(true, mCodec).build();
-    }
-
-    private String getMediaDescription(SdpSessionDescription.AudioCodec codec) {
-        return String.format("%d %s/%d", codec.payloadType, codec.name,
-                codec.sampleRate);
-    }
-
-    private long getSessionId() {
-        if (mSessionId < 0) {
-            mSessionId = System.currentTimeMillis();
-        }
-        return mSessionId;
-    }
-
-    private SdpSessionDescription.Builder createSdpBuilder(
-            boolean addTelephoneEvent,
-            SdpSessionDescription.AudioCodec... codecs) {
-        String localIp = getLocalIp();
-        SdpSessionDescription.Builder sdpBuilder;
-        try {
-            long sessionVersion = System.currentTimeMillis();
-            sdpBuilder = new SdpSessionDescription.Builder("SIP Call")
-                    .setOrigin(mLocalProfile, getSessionId(), sessionVersion,
-                            SDPKeywords.IN, SDPKeywords.IPV4, localIp)
-                    .setConnectionInfo(SDPKeywords.IN, SDPKeywords.IPV4,
-                            localIp);
-            List<Integer> codecIds = new ArrayList<Integer>();
-            for (SdpSessionDescription.AudioCodec codec : codecs) {
-                codecIds.add(codec.payloadType);
-            }
-            if (addTelephoneEvent) codecIds.add(DTMF);
-            sdpBuilder.addMedia(AUDIO, getLocalMediaPort(), 1, "RTP/AVP",
-                    codecIds.toArray(new Integer[codecIds.size()]));
-            for (SdpSessionDescription.AudioCodec codec : codecs) {
-                sdpBuilder.addMediaAttribute(AUDIO, "rtpmap",
-                        getMediaDescription(codec));
-            }
-            if (addTelephoneEvent) {
-                sdpBuilder.addMediaAttribute(AUDIO, "rtpmap",
-                        DTMF + " telephone-event/8000");
-            }
-            // FIXME: deal with vbr codec
-            sdpBuilder.addMediaAttribute(AUDIO, "ptime", "20");
-        } catch (SdpException e) {
-            throw new RuntimeException(e);
-        }
-        return sdpBuilder;
-    }
-
-    public synchronized void toggleMute() {
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) {
-            audioGroup.setMode(
-                    mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED);
-            mMuted = !mMuted;
-        }
-    }
-
-    public synchronized boolean isMuted() {
-        return mMuted;
-    }
-
-    public synchronized void setSpeakerMode(boolean speakerMode) {
-        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
-                .setSpeakerphoneOn(speakerMode);
-    }
-
-    public void sendDtmf(int code) {
-        sendDtmf(code, null);
-    }
-
-    public synchronized void sendDtmf(int code, Message result) {
-        AudioGroup audioGroup = getAudioGroup();
-        if ((audioGroup != null) && (mSipSession != null)
-                && (SipSessionState.IN_CALL == getState())) {
-            Log.v(TAG, "send DTMF: " + code);
-            audioGroup.sendDtmf(code);
-        }
-        if (result != null) result.sendToTarget();
-    }
-
-    public synchronized AudioStream getAudioStream() {
-        return mAudioStream;
-    }
-
-    public synchronized AudioGroup getAudioGroup() {
-        if (mAudioGroup != null) return mAudioGroup;
-        return ((mAudioStream == null) ? null : mAudioStream.getAudioGroup());
-    }
-
-    public synchronized void setAudioGroup(AudioGroup group) {
-        if ((mAudioStream != null) && (mAudioStream.getAudioGroup() != null)) {
-            mAudioStream.join(group);
-        }
-        mAudioGroup = group;
-    }
-
-    private SdpSessionDescription.AudioCodec getCodec(SdpSessionDescription sd) {
-        HashMap<String, AudioCodec> acceptableCodecs =
-                new HashMap<String, AudioCodec>();
-        for (AudioCodec codec : AudioCodec.getSystemSupportedCodecs()) {
-            acceptableCodecs.put(codec.name, codec);
-        }
-        for (SdpSessionDescription.AudioCodec codec : sd.getAudioCodecs()) {
-            AudioCodec matchedCodec = acceptableCodecs.get(codec.name);
-            if (matchedCodec != null) return codec;
-        }
-        Log.w(TAG, "no common codec is found, use PCM/0");
-        return convert(AudioCodec.ULAW);
-    }
-
-    private AudioCodec convert(SdpSessionDescription.AudioCodec codec) {
-        AudioCodec c = AudioCodec.getSystemSupportedCodec(codec.name);
-        return ((c == null) ? AudioCodec.ULAW : c);
-    }
-
-    private SdpSessionDescription.AudioCodec convert(AudioCodec codec) {
-        return new SdpSessionDescription.AudioCodec(codec.defaultType,
-                codec.name, codec.sampleRate, codec.sampleCount);
-    }
-
-    private SdpSessionDescription.AudioCodec[] convert(AudioCodec[] codecs) {
-        SdpSessionDescription.AudioCodec[] copies =
-                new SdpSessionDescription.AudioCodec[codecs.length];
-        for (int i = 0, len = codecs.length; i < len; i++) {
-            copies[i] = convert(codecs[i]);
-        }
-        return copies;
-    }
-
-    public void startAudio() {
-        try {
-            startAudioInternal();
-        } catch (UnknownHostException e) {
-            onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE,
-                    e.getMessage());
-        } catch (Throwable e) {
-            onError(mSipSession, SipErrorCode.CLIENT_ERROR,
-                    e.getMessage());
-        }
-    }
-
-    private synchronized void startAudioInternal() throws UnknownHostException {
-        stopCall(DONT_RELEASE_SOCKET);
-        mInCall = true;
-        SdpSessionDescription peerSd = mPeerSd;
-        if (isWifiOn()) grabWifiHighPerfLock();
-        String peerMediaAddress = peerSd.getPeerMediaAddress(AUDIO);
-        // TODO: handle multiple media fields
-        int peerMediaPort = peerSd.getPeerMediaPort(AUDIO);
-        Log.i(TAG, "start audiocall " + peerMediaAddress + ":" + peerMediaPort);
-
-        int localPort = getLocalMediaPort();
-        int sampleRate = 8000;
-        int frameSize = sampleRate / 50; // 160
-
-        // TODO: get sample rate from sdp
-        mCodec = getCodec(peerSd);
-
-        AudioStream audioStream = mAudioStream;
-        audioStream.associate(InetAddress.getByName(peerMediaAddress),
-                peerMediaPort);
-        audioStream.setCodec(convert(mCodec), mCodec.payloadType);
-        audioStream.setDtmfType(DTMF);
-        Log.d(TAG, "start media: localPort=" + localPort + ", peer="
-                + peerMediaAddress + ":" + peerMediaPort);
-
-        audioStream.setMode(RtpStream.MODE_NORMAL);
-        if (!mHold) {
-            // FIXME: won't work if peer is not sending nor receiving
-            if (!peerSd.isSending(AUDIO)) {
-                Log.d(TAG, "   not receiving");
-                audioStream.setMode(RtpStream.MODE_SEND_ONLY);
-            }
-            if (!peerSd.isReceiving(AUDIO)) {
-                Log.d(TAG, "   not sending");
-                audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
-            }
-
-            /* The recorder volume will be very low if the device is in
-             * IN_CALL mode. Therefore, we have to set the mode to NORMAL
-             * in order to have the normal microphone level.
-             */
-            ((AudioManager) mContext.getSystemService
-                    (Context.AUDIO_SERVICE))
-                    .setMode(AudioManager.MODE_NORMAL);
-        }
-
-        // AudioGroup logic:
-        AudioGroup audioGroup = getAudioGroup();
-        if (mHold) {
-            if (audioGroup != null) {
-                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-            }
-            // don't create an AudioGroup here; doing so will fail if
-            // there's another AudioGroup out there that's active
-        } else {
-            if (audioGroup == null) audioGroup = new AudioGroup();
-            audioStream.join(audioGroup);
-            if (mMuted) {
-                audioGroup.setMode(AudioGroup.MODE_MUTED);
-            } else {
-                audioGroup.setMode(AudioGroup.MODE_NORMAL);
-            }
-        }
-    }
-
-    private void stopCall(boolean releaseSocket) {
-        Log.d(TAG, "stop audiocall");
-        releaseWifiHighPerfLock();
-        if (mAudioStream != null) {
-            mAudioStream.join(null);
-
-            if (releaseSocket) {
-                mAudioStream.release();
-                mAudioStream = null;
-            }
-        }
-    }
-
-    private int getLocalMediaPort() {
-        if (mAudioStream != null) return mAudioStream.getLocalPort();
-        try {
-            AudioStream s = mAudioStream =
-                    new AudioStream(InetAddress.getByName(getLocalIp()));
-            return s.getLocalPort();
-        } catch (IOException e) {
-            Log.w(TAG, "getLocalMediaPort(): " + e);
-            throw new RuntimeException(e);
-        }
-    }
-
-    private String getLocalIp() {
-        try {
-            return mSipSession.getLocalIp();
-        } catch (RemoteException e) {
-            // FIXME
-            return "127.0.0.1";
-        }
-    }
-
-    public synchronized void setRingbackToneEnabled(boolean enabled) {
-        mRingbackToneEnabled = enabled;
-    }
-
-    public synchronized void setRingtoneEnabled(boolean enabled) {
-        mRingtoneEnabled = enabled;
-    }
-
-    private void startRingbackTone() {
-        if (!mRingbackToneEnabled) return;
-        if (mRingbackTone == null) {
-            // The volume relative to other sounds in the stream
-            int toneVolume = 80;
-            mRingbackTone = new ToneGenerator(
-                    AudioManager.STREAM_VOICE_CALL, toneVolume);
-        }
-        mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
-    }
-
-    private void stopRingbackTone() {
-        if (mRingbackTone != null) {
-            mRingbackTone.stopTone();
-            mRingbackTone.release();
-            mRingbackTone = null;
-        }
-    }
-
-    private void startRinging() {
-        if (!mRingtoneEnabled) return;
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .vibrate(new long[] {0, 1000, 1000}, 1);
-        AudioManager am = (AudioManager)
-                mContext.getSystemService(Context.AUDIO_SERVICE);
-        if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
-            String ringtoneUri =
-                    Settings.System.DEFAULT_RINGTONE_URI.toString();
-            mRingtone = RingtoneManager.getRingtone(mContext,
-                    Uri.parse(ringtoneUri));
-            mRingtone.play();
-        }
-    }
-
-    private void stopRinging() {
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .cancel();
-        if (mRingtone != null) mRingtone.stop();
-    }
-
-    private void throwSipException(Throwable throwable) throws SipException {
-        if (throwable instanceof SipException) {
-            throw (SipException) throwable;
-        } else {
-            throw new SipException("", throwable);
-        }
-    }
-
-    private SipProfile getPeerProfile(ISipSession session) {
-        try {
-            return session.getPeerProfile();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-}
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
index 7496d28..eb7a1ae 100644
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ b/voip/java/android/net/sip/SipErrorCode.java
@@ -58,6 +58,9 @@
     /** When data connection is lost. */
     public static final int DATA_CONNECTION_LOST = -10;
 
+    /** Cross-domain authentication required. */
+    public static final int CROSS_DOMAIN_AUTHENTICATION = -11;
+
     public static String toString(int errorCode) {
         switch (errorCode) {
             case NO_ERROR:
@@ -82,6 +85,8 @@
                 return "IN_PROGRESS";
             case DATA_CONNECTION_LOST:
                 return "DATA_CONNECTION_LOST";
+            case CROSS_DOMAIN_AUTHENTICATION:
+                return "CROSS_DOMAIN_AUTHENTICATION";
             default:
                 return "UNKNOWN";
         }
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 31768d7..59631c1 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -30,8 +30,9 @@
  * The class provides API for various SIP related tasks. Specifically, the API
  * allows an application to:
  * <ul>
- * <li>register a {@link SipProfile} to have the background SIP service listen
- *      to incoming calls and broadcast them with registered command string. See
+ * <li>open a {@link SipProfile} to get ready for making outbound calls or have
+ *      the background SIP service listen to incoming calls and broadcast them
+ *      with registered command string. See
  *      {@link #open(SipProfile, String, SipRegistrationListener)},
  *      {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
  *      {@link #isRegistered}. It also facilitates handling of the incoming call
@@ -40,39 +41,59 @@
  *      {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li>
  * <li>make/take SIP-based audio calls. See
  *      {@link #makeAudioCall} and {@link #takeAudioCall}.</li>
- * <li>register/unregister with a SIP service provider. See
+ * <li>register/unregister with a SIP service provider manually. See
  *      {@link #register} and {@link #unregister}.</li>
- * <li>process SIP events directly with a {@link ISipSession} created by
+ * <li>process SIP events directly with a {@link SipSession} created by
  *      {@link #createSipSession}.</li>
  * </ul>
  * @hide
  */
 public class SipManager {
-    /** @hide */
-    public static final String SIP_INCOMING_CALL_ACTION =
+    /**
+     * Action string for the incoming call intent for the Phone app.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_INCOMING_CALL =
             "com.android.phone.SIP_INCOMING_CALL";
-    /** @hide */
-    public static final String SIP_ADD_PHONE_ACTION =
+    /**
+     * Action string for the add-phone intent.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_ADD_PHONE =
             "com.android.phone.SIP_ADD_PHONE";
-    /** @hide */
-    public static final String SIP_REMOVE_PHONE_ACTION =
+    /**
+     * Action string for the remove-phone intent.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_REMOVE_PHONE =
             "com.android.phone.SIP_REMOVE_PHONE";
-    /** @hide */
-    public static final String LOCAL_URI_KEY = "LOCAL SIPURI";
+    /**
+     * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents.
+     * Internal use only.
+     * @hide
+     */
+    public static final String EXTRA_LOCAL_URI = "android:localSipUri";
 
-    private static final String CALL_ID_KEY = "CallID";
-    private static final String OFFER_SD_KEY = "OfferSD";
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_CALL_ID = "android:sipCallID";
+
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
 
     private ISipService mSipService;
+    private Context mContext;
 
     /**
-     * Gets a manager instance. Returns null if SIP API is not supported.
+     * Creates a manager instance. Returns null if SIP API is not supported.
      *
-     * @param context application context for checking if SIP API is supported
+     * @param context application context for creating the manager object
      * @return the manager instance or null if SIP API is not supported
      */
-    public static SipManager getInstance(Context context) {
-        return (isApiSupported(context) ? new SipManager() : null);
+    public static SipManager newInstance(Context context) {
+        return (isApiSupported(context) ? new SipManager(context) : null);
     }
 
     /**
@@ -80,7 +101,7 @@
      */
     public static boolean isApiSupported(Context context) {
         return true;
-        /* 
+        /* TODO: uncomment this before ship
         return context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SIP);
          */
@@ -91,7 +112,7 @@
      */
     public static boolean isVoipSupported(Context context) {
         return true;
-        /* 
+        /* TODO: uncomment this before ship
         return context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
          */
@@ -105,23 +126,21 @@
                 com.android.internal.R.bool.config_sip_wifi_only);
     }
 
-    private SipManager() {
+    private SipManager(Context context) {
+        mContext = context;
         createSipService();
     }
 
     private void createSipService() {
-        if (mSipService != null) return;
         IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
         mSipService = ISipService.Stub.asInterface(b);
     }
 
     /**
-     * Opens the profile for making calls and/or receiving calls. Subsequent
-     * SIP calls can be made through the default phone UI. The caller may also
-     * make subsequent calls through {@link #makeAudioCall}.
-     * If the receiving-call option is enabled in the profile, the SIP service
-     * will register the profile to the corresponding server periodically in
-     * order to receive calls from the server.
+     * Opens the profile for making calls. The caller may make subsequent calls
+     * through {@link #makeAudioCall}. If one also wants to receive calls on the
+     * profile, use {@link #open(SipProfile, String, SipRegistrationListener)}
+     * instead.
      *
      * @param localProfile the SIP profile to make calls from
      * @throws SipException if the profile contains incorrect settings or
@@ -136,12 +155,11 @@
     }
 
     /**
-     * Opens the profile for making calls and/or receiving calls. Subsequent
-     * SIP calls can be made through the default phone UI. The caller may also
-     * make subsequent calls through {@link #makeAudioCall}.
-     * If the receiving-call option is enabled in the profile, the SIP service
-     * will register the profile to the corresponding server periodically in
-     * order to receive calls from the server.
+     * Opens the profile for making calls and/or receiving calls. The caller may
+     * make subsequent calls through {@link #makeAudioCall}. If the
+     * auto-registration option is enabled in the profile, the SIP service
+     * will register the profile to the corresponding SIP provider periodically
+     * in order to receive calls from the provider.
      *
      * @param localProfile the SIP profile to receive incoming calls for
      * @param incomingCallBroadcastAction the action to be broadcast when an
@@ -195,7 +213,8 @@
     }
 
     /**
-     * Checks if the specified profile is enabled to receive calls.
+     * Checks if the specified profile is opened in the SIP service for
+     * making and/or receiving calls.
      *
      * @param localProfileUri the URI of the profile in question
      * @return true if the profile is enabled to receive calls
@@ -210,11 +229,16 @@
     }
 
     /**
-     * Checks if the specified profile is registered to the server for
-     * receiving calls.
+     * Checks if the SIP service has successfully registered the profile to the
+     * SIP provider (specified in the profile) for receiving calls. Returning
+     * true from this method also implies the profile is opened
+     * ({@link #isOpened}).
      *
      * @param localProfileUri the URI of the profile in question
-     * @return true if the profile is registered to the server
+     * @return true if the profile is registered to the SIP provider; false if
+     *        the profile has not been opened in the SIP service or the SIP
+     *        service has not yet successfully registered the profile to the SIP
+     *        provider
      * @throws SipException if calling the SIP service results in an error
      */
     public boolean isRegistered(String localProfileUri) throws SipException {
@@ -231,7 +255,6 @@
      * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param localProfile the SIP profile to make the call from
      * @param peerProfile the SIP profile to make the call to
      * @param listener to listen to the call events from {@link SipAudioCall};
@@ -241,12 +264,17 @@
      * @throws SipException if calling the SIP service results in an error
      * @see SipAudioCall.Listener.onError
      */
-    public SipAudioCall makeAudioCall(Context context, SipProfile localProfile,
+    public SipAudioCall makeAudioCall(SipProfile localProfile,
             SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
             throws SipException {
-        SipAudioCall call = new SipAudioCallImpl(context, localProfile);
+        SipAudioCall call = new SipAudioCall(mContext, localProfile);
         call.setListener(listener);
-        call.makeCall(peerProfile, this, timeout);
+        SipSession s = createSipSession(localProfile, null);
+        if (s == null) {
+            throw new SipException(
+                    "Failed to create SipSession; network available?");
+        }
+        call.makeCall(peerProfile, s, timeout);
         return call;
     }
 
@@ -257,7 +285,6 @@
      * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param localProfileUri URI of the SIP profile to make the call from
      * @param peerProfileUri URI of the SIP profile to make the call to
      * @param listener to listen to the call events from {@link SipAudioCall};
@@ -267,11 +294,11 @@
      * @throws SipException if calling the SIP service results in an error
      * @see SipAudioCall.Listener.onError
      */
-    public SipAudioCall makeAudioCall(Context context, String localProfileUri,
+    public SipAudioCall makeAudioCall(String localProfileUri,
             String peerProfileUri, SipAudioCall.Listener listener, int timeout)
             throws SipException {
         try {
-            return makeAudioCall(context,
+            return makeAudioCall(
                     new SipProfile.Builder(localProfileUri).build(),
                     new SipProfile.Builder(peerProfileUri).build(), listener,
                     timeout);
@@ -281,15 +308,14 @@
     }
 
     /**
-     * The method calls {@code takeAudioCall(context, incomingCallIntent,
+     * The method calls {@code takeAudioCall(incomingCallIntent,
      * listener, true}.
      *
-     * @see #takeAudioCall(Context, Intent, SipAudioCall.Listener, boolean)
+     * @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean)
      */
-    public SipAudioCall takeAudioCall(Context context,
-            Intent incomingCallIntent, SipAudioCall.Listener listener)
-            throws SipException {
-        return takeAudioCall(context, incomingCallIntent, listener, true);
+    public SipAudioCall takeAudioCall(Intent incomingCallIntent,
+            SipAudioCall.Listener listener) throws SipException {
+        return takeAudioCall(incomingCallIntent, listener, true);
     }
 
     /**
@@ -298,16 +324,15 @@
      * {@link SipAudioCall.Listener#onRinging}
      * callback.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param incomingCallIntent the incoming call broadcast intent
      * @param listener to listen to the call events from {@link SipAudioCall};
      *      can be null
      * @return a {@link SipAudioCall} object
      * @throws SipException if calling the SIP service results in an error
      */
-    public SipAudioCall takeAudioCall(Context context,
-            Intent incomingCallIntent, SipAudioCall.Listener listener,
-            boolean ringtoneEnabled) throws SipException {
+    public SipAudioCall takeAudioCall(Intent incomingCallIntent,
+            SipAudioCall.Listener listener, boolean ringtoneEnabled)
+            throws SipException {
         if (incomingCallIntent == null) return null;
 
         String callId = getCallId(incomingCallIntent);
@@ -324,10 +349,10 @@
         try {
             ISipSession session = mSipService.getPendingSession(callId);
             if (session == null) return null;
-            SipAudioCall call = new SipAudioCallImpl(
-                    context, session.getLocalProfile());
+            SipAudioCall call = new SipAudioCall(
+                    mContext, session.getLocalProfile());
             call.setRingtoneEnabled(ringtoneEnabled);
-            call.attachCall(session, offerSd);
+            call.attachCall(new SipSession(session), offerSd);
             call.setListener(listener);
             return call;
         } catch (Throwable t) {
@@ -355,7 +380,7 @@
      * @return the call ID or null if the intent does not contain it
      */
     public static String getCallId(Intent incomingCallIntent) {
-        return incomingCallIntent.getStringExtra(CALL_ID_KEY);
+        return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
     }
 
     /**
@@ -367,30 +392,30 @@
      *      have it
      */
     public static String getOfferSessionDescription(Intent incomingCallIntent) {
-        return incomingCallIntent.getStringExtra(OFFER_SD_KEY);
+        return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
     }
 
     /**
      * Creates an incoming call broadcast intent.
      *
-     * @param action the action string to broadcast
      * @param callId the call ID of the incoming call
      * @param sessionDescription the session description of the incoming call
      * @return the incoming call intent
      * @hide
      */
-    public static Intent createIncomingCallBroadcast(String action,
-            String callId, String sessionDescription) {
-        Intent intent = new Intent(action);
-        intent.putExtra(CALL_ID_KEY, callId);
-        intent.putExtra(OFFER_SD_KEY, sessionDescription);
+    public static Intent createIncomingCallBroadcast(String callId,
+            String sessionDescription) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_CALL_ID, callId);
+        intent.putExtra(EXTRA_OFFER_SD, sessionDescription);
         return intent;
     }
 
     /**
-     * Registers the profile to the corresponding server for receiving calls.
-     * {@link #open} is still needed to be called at least once in order for
-     * the SIP service to broadcast an intent when an incoming call is received.
+     * Manually registers the profile to the corresponding SIP provider for
+     * receiving calls. {@link #open(SipProfile, String, SipRegistrationListener)}
+     * is still needed to be called at least once in order for the SIP service
+     * to broadcast an intent when an incoming call is received.
      *
      * @param localProfile the SIP profile to register with
      * @param expiryTime registration expiration time (in seconds)
@@ -409,8 +434,10 @@
     }
 
     /**
-     * Unregisters the profile from the corresponding server for not receiving
-     * further calls.
+     * Manually unregisters the profile from the corresponding SIP provider for
+     * stop receiving further calls. This may interference with the auto
+     * registration process in the SIP service if the auto-registration option
+     * in the profile is enabled.
      *
      * @param localProfile the SIP profile to register with
      * @param listener to listen to the registration events
@@ -460,10 +487,11 @@
      * @param localProfile the SIP profile the session is associated with
      * @param listener to listen to SIP session events
      */
-    public ISipSession createSipSession(SipProfile localProfile,
-            ISipSessionListener listener) throws SipException {
+    public SipSession createSipSession(SipProfile localProfile,
+            SipSession.Listener listener) throws SipException {
         try {
-            return mSipService.createSession(localProfile, listener);
+            ISipSession s = mSipService.createSession(localProfile, null);
+            return new SipSession(s, listener);
         } catch (RemoteException e) {
             throw new SipException("createSipSession()", e);
         }
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index 88bfba9..6d5cb3c 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -48,7 +48,6 @@
     private boolean mAutoRegistration = true;
     private transient int mCallingUid = 0;
 
-    /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
             new Parcelable.Creator<SipProfile>() {
                 public SipProfile createFromParcel(Parcel in) {
@@ -287,7 +286,7 @@
         mCallingUid = in.readInt();
     }
 
-    /** @hide */
+    @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeSerializable(mAddress);
         out.writeString(mProxyAddress);
@@ -300,7 +299,7 @@
         out.writeInt(mCallingUid);
     }
 
-    /** @hide */
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java
new file mode 100644
index 0000000..0cc7206
--- /dev/null
+++ b/voip/java/android/net/sip/SipSession.java
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.sip;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A SIP session that is associated with a SIP dialog or a standalone
+ * transaction not within a dialog.
+ * @hide
+ */
+public final class SipSession {
+    private static final String TAG = "SipSession";
+
+    /**
+     * Defines {@link SipSession} states.
+     * @hide
+     */
+    public static class State {
+        /** When session is ready to initiate a call or transaction. */
+        public static final int READY_TO_CALL = 0;
+
+        /** When the registration request is sent out. */
+        public static final int REGISTERING = 1;
+
+        /** When the unregistration request is sent out. */
+        public static final int DEREGISTERING = 2;
+
+        /** When an INVITE request is received. */
+        public static final int INCOMING_CALL = 3;
+
+        /** When an OK response is sent for the INVITE request received. */
+        public static final int INCOMING_CALL_ANSWERING = 4;
+
+        /** When an INVITE request is sent. */
+        public static final int OUTGOING_CALL = 5;
+
+        /** When a RINGING response is received for the INVITE request sent. */
+        public static final int OUTGOING_CALL_RING_BACK = 6;
+
+        /** When a CANCEL request is sent for the INVITE request sent. */
+        public static final int OUTGOING_CALL_CANCELING = 7;
+
+        /** When a call is established. */
+        public static final int IN_CALL = 8;
+
+        /** When an OPTIONS request is sent. */
+        public static final int PINGING = 9;
+
+        /** Not defined. */
+        public static final int NOT_DEFINED = 101;
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case READY_TO_CALL:
+                    return "READY_TO_CALL";
+                case REGISTERING:
+                    return "REGISTERING";
+                case DEREGISTERING:
+                    return "DEREGISTERING";
+                case INCOMING_CALL:
+                    return "INCOMING_CALL";
+                case INCOMING_CALL_ANSWERING:
+                    return "INCOMING_CALL_ANSWERING";
+                case OUTGOING_CALL:
+                    return "OUTGOING_CALL";
+                case OUTGOING_CALL_RING_BACK:
+                    return "OUTGOING_CALL_RING_BACK";
+                case OUTGOING_CALL_CANCELING:
+                    return "OUTGOING_CALL_CANCELING";
+                case IN_CALL:
+                    return "IN_CALL";
+                case PINGING:
+                    return "PINGING";
+                default:
+                    return "NOT_DEFINED";
+            }
+        }
+
+        private State() {
+        }
+    }
+
+    /**
+     * Listener class that listens to {@link SipSession} events.
+     * @hide
+     */
+    public static class Listener {
+        /**
+         * Called when an INVITE request is sent to initiate a new call.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onCalling(SipSession session) {
+        }
+
+        /**
+         * Called when an INVITE request is received.
+         *
+         * @param session the session object that carries out the transaction
+         * @param caller the SIP profile of the caller
+         * @param sessionDescription the caller's session description
+         */
+        public void onRinging(SipSession session, SipProfile caller,
+                String sessionDescription) {
+        }
+
+        /**
+         * Called when a RINGING response is received for the INVITE request sent
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRingingBack(SipSession session) {
+        }
+
+        /**
+         * Called when the session is established.
+         *
+         * @param session the session object that is associated with the dialog
+         * @param sessionDescription the peer's session description
+         */
+        public void onCallEstablished(SipSession session,
+                String sessionDescription) {
+        }
+
+        /**
+         * Called when the session is terminated.
+         *
+         * @param session the session object that is associated with the dialog
+         */
+        public void onCallEnded(SipSession session) {
+        }
+
+        /**
+         * Called when the peer is busy during session initialization.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onCallBusy(SipSession session) {
+        }
+
+        /**
+         * Called when an error occurs during session initialization and
+         * termination.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onError(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when an error occurs during session modification negotiation.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onCallChangeFailed(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when a registration request is sent.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRegistering(SipSession session) {
+        }
+
+        /**
+         * Called when registration is successfully done.
+         *
+         * @param session the session object that carries out the transaction
+         * @param duration duration in second before the registration expires
+         */
+        public void onRegistrationDone(SipSession session, int duration) {
+        }
+
+        /**
+         * Called when the registration fails.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onRegistrationFailed(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when the registration gets timed out.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRegistrationTimeout(SipSession session) {
+        }
+    }
+
+    private final ISipSession mSession;
+    private Listener mListener;
+
+    SipSession(ISipSession realSession) {
+        mSession = realSession;
+        if (realSession != null) {
+            try {
+                realSession.setListener(createListener());
+            } catch (RemoteException e) {
+                Log.e(TAG, "SipSession.setListener(): " + e);
+            }
+        }
+    }
+
+    SipSession(ISipSession realSession, Listener listener) {
+        this(realSession);
+        setListener(listener);
+    }
+
+    /**
+     * Gets the IP address of the local host on which this SIP session runs.
+     *
+     * @return the IP address of the local host
+     */
+    public String getLocalIp() {
+        try {
+            return mSession.getLocalIp();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getLocalIp(): " + e);
+            return "127.0.0.1";
+        }
+    }
+
+    /**
+     * Gets the SIP profile that this session is associated with.
+     *
+     * @return the SIP profile that this session is associated with
+     */
+    public SipProfile getLocalProfile() {
+        try {
+            return mSession.getLocalProfile();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getLocalProfile(): " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Gets the SIP profile that this session is connected to. Only available
+     * when the session is associated with a SIP dialog.
+     *
+     * @return the SIP profile that this session is connected to
+     */
+    public SipProfile getPeerProfile() {
+        try {
+            return mSession.getPeerProfile();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getPeerProfile(): " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Gets the session state. The value returned must be one of the states in
+     * {@link SipSessionState}.
+     *
+     * @return the session state
+     */
+    public int getState() {
+        try {
+            return mSession.getState();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getState(): " + e);
+            return State.NOT_DEFINED;
+        }
+    }
+
+    /**
+     * Checks if the session is in a call.
+     *
+     * @return true if the session is in a call
+     */
+    public boolean isInCall() {
+        try {
+            return mSession.isInCall();
+        } catch (RemoteException e) {
+            Log.e(TAG, "isInCall(): " + e);
+            return false;
+        }
+    }
+
+    /**
+     * Gets the call ID of the session.
+     *
+     * @return the call ID
+     */
+    public String getCallId() {
+        try {
+            return mSession.getCallId();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getCallId(): " + e);
+            return null;
+        }
+    }
+
+
+    /**
+     * Sets the listener to listen to the session events. A {@code SipSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+
+    /**
+     * Performs registration to the server specified by the associated local
+     * profile. The session listener is called back upon success or failure of
+     * registration. The method is only valid to call when the session state is
+     * in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @param duration duration in second before the registration expires
+     * @see Listener
+     */
+    public void register(int duration) {
+        try {
+            mSession.register(duration);
+        } catch (RemoteException e) {
+            Log.e(TAG, "register(): " + e);
+        }
+    }
+
+    /**
+     * Performs unregistration to the server specified by the associated local
+     * profile. Unregistration is technically the same as registration with zero
+     * expiration duration. The session listener is called back upon success or
+     * failure of unregistration. The method is only valid to call when the
+     * session state is in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @see Listener
+     */
+    public void unregister() {
+        try {
+            mSession.unregister();
+        } catch (RemoteException e) {
+            Log.e(TAG, "unregister(): " + e);
+        }
+    }
+
+    /**
+     * Initiates a call to the specified profile. The session listener is called
+     * back upon defined session events. The method is only valid to call when
+     * the session state is in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @param callee the SIP profile to make the call to
+     * @param sessionDescription the session description of this call
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     * @see Listener
+     */
+    public void makeCall(SipProfile callee, String sessionDescription,
+            int timeout) {
+        try {
+            mSession.makeCall(callee, sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "makeCall(): " + e);
+        }
+    }
+
+    /**
+     * Answers an incoming call with the specified session description. The
+     * method is only valid to call when the session state is in
+     * {@link SipSessionState#INCOMING_CALL}.
+     *
+     * @param sessionDescription the session description to answer this call
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     */
+    public void answerCall(String sessionDescription, int timeout) {
+        try {
+            mSession.answerCall(sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "answerCall(): " + e);
+        }
+    }
+
+    /**
+     * Ends an established call, terminates an outgoing call or rejects an
+     * incoming call. The method is only valid to call when the session state is
+     * in {@link SipSessionState#IN_CALL},
+     * {@link SipSessionState#INCOMING_CALL},
+     * {@link SipSessionState#OUTGOING_CALL} or
+     * {@link SipSessionState#OUTGOING_CALL_RING_BACK}.
+     */
+    public void endCall() {
+        try {
+            mSession.endCall();
+        } catch (RemoteException e) {
+            Log.e(TAG, "endCall(): " + e);
+        }
+    }
+
+    /**
+     * Changes the session description during a call. The method is only valid
+     * to call when the session state is in {@link SipSessionState#IN_CALL}.
+     *
+     * @param sessionDescription the new session description
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     */
+    public void changeCall(String sessionDescription, int timeout) {
+        try {
+            mSession.changeCall(sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "changeCall(): " + e);
+        }
+    }
+
+    ISipSession getRealSession() {
+        return mSession;
+    }
+
+    private ISipSessionListener createListener() {
+        return new ISipSessionListener.Stub() {
+            public void onCalling(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCalling(SipSession.this);
+                }
+            }
+
+            public void onRinging(ISipSession session, SipProfile caller,
+                    String sessionDescription) {
+                if (mListener != null) {
+                    mListener.onRinging(SipSession.this, caller,
+                            sessionDescription);
+                }
+            }
+
+            public void onRingingBack(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRingingBack(SipSession.this);
+                }
+            }
+
+            public void onCallEstablished(ISipSession session,
+                    String sessionDescription) {
+                if (mListener != null) {
+                    mListener.onCallEstablished(SipSession.this,
+                            sessionDescription);
+                }
+            }
+
+            public void onCallEnded(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCallEnded(SipSession.this);
+                }
+            }
+
+            public void onCallBusy(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCallBusy(SipSession.this);
+                }
+            }
+
+            public void onCallChangeFailed(ISipSession session, int errorCode,
+                    String message) {
+                if (mListener != null) {
+                    mListener.onCallChangeFailed(SipSession.this, errorCode,
+                            message);
+                }
+            }
+
+            public void onError(ISipSession session, int errorCode, String message) {
+                if (mListener != null) {
+                    mListener.onError(SipSession.this, errorCode, message);
+                }
+            }
+
+            public void onRegistering(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRegistering(SipSession.this);
+                }
+            }
+
+            public void onRegistrationDone(ISipSession session, int duration) {
+                if (mListener != null) {
+                    mListener.onRegistrationDone(SipSession.this, duration);
+                }
+            }
+
+            public void onRegistrationFailed(ISipSession session, int errorCode,
+                    String message) {
+                if (mListener != null) {
+                    mListener.onRegistrationFailed(SipSession.this, errorCode,
+                            message);
+                }
+            }
+
+            public void onRegistrationTimeout(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRegistrationTimeout(SipSession.this);
+                }
+            }
+        };
+    }
+}
diff --git a/voip/java/android/net/sip/SipSessionState.java b/voip/java/android/net/sip/SipSessionState.java
deleted file mode 100644
index 31e9d3f..0000000
--- a/voip/java/android/net/sip/SipSessionState.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-/**
- * Defines {@link ISipSession} states.
- * @hide
- */
-public class SipSessionState {
-    /** When session is ready to initiate a call or transaction. */
-    public static final int READY_TO_CALL = 0;
-
-    /** When the registration request is sent out. */
-    public static final int REGISTERING = 1;
-
-    /** When the unregistration request is sent out. */
-    public static final int DEREGISTERING = 2;
-
-    /** When an INVITE request is received. */
-    public static final int INCOMING_CALL = 3;
-
-    /** When an OK response is sent for the INVITE request received. */
-    public static final int INCOMING_CALL_ANSWERING = 4;
-
-    /** When an INVITE request is sent. */
-    public static final int OUTGOING_CALL = 5;
-
-    /** When a RINGING response is received for the INVITE request sent. */
-    public static final int OUTGOING_CALL_RING_BACK = 6;
-
-    /** When a CANCEL request is sent for the INVITE request sent. */
-    public static final int OUTGOING_CALL_CANCELING = 7;
-
-    /** When a call is established. */
-    public static final int IN_CALL = 8;
-
-    /** Some error occurs when making a remote call to {@link ISipSession}. */
-    public static final int REMOTE_ERROR = 9;
-
-    /** When an OPTIONS request is sent. */
-    public static final int PINGING = 10;
-
-    /** Not defined. */
-    public static final int NOT_DEFINED = 101;
-
-    /**
-     * Converts the state to string.
-     */
-    public static String toString(int state) {
-        switch (state) {
-            case READY_TO_CALL:
-                return "READY_TO_CALL";
-            case REGISTERING:
-                return "REGISTERING";
-            case DEREGISTERING:
-                return "DEREGISTERING";
-            case INCOMING_CALL:
-                return "INCOMING_CALL";
-            case INCOMING_CALL_ANSWERING:
-                return "INCOMING_CALL_ANSWERING";
-            case OUTGOING_CALL:
-                return "OUTGOING_CALL";
-            case OUTGOING_CALL_RING_BACK:
-                return "OUTGOING_CALL_RING_BACK";
-            case OUTGOING_CALL_CANCELING:
-                return "OUTGOING_CALL_CANCELING";
-            case IN_CALL:
-                return "IN_CALL";
-            case REMOTE_ERROR:
-                return "REMOTE_ERROR";
-            case PINGING:
-                return "PINGING";
-            default:
-                return "NOT_DEFINED";
-        }
-    }
-
-    private SipSessionState() {
-    }
-}
diff --git a/services/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
similarity index 100%
rename from services/java/com/android/server/sip/SipHelper.java
rename to voip/java/com/android/server/sip/SipHelper.java
diff --git a/services/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
similarity index 96%
rename from services/java/com/android/server/sip/SipService.java
rename to voip/java/com/android/server/sip/SipService.java
index a2ebc69..0ff5586 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -30,8 +30,8 @@
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
+import android.net.sip.SipSession;
 import android.net.sip.SipSessionAdapter;
-import android.net.sip.SipSessionState;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Bundle;
@@ -40,6 +40,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
@@ -90,12 +91,14 @@
     private ConnectivityReceiver mConnectivityReceiver;
 
     /**
-     * Creates a {@code SipService} instance. Returns null if SIP API is not
-     * supported.
+     * Starts the SIP service. Do nothing if the SIP API is not supported on the
+     * device.
      */
-    public static SipService create(Context context) {
-        return (SipManager.isApiSupported(context) ? new SipService(context)
-                                                   : null);
+    public static void start(Context context) {
+        if (SipManager.isApiSupported(context)) {
+            ServiceManager.addService("sip", new SipService(context));
+            Log.i(TAG, "SIP service started");
+        }
     }
 
     private SipService(Context context) {
@@ -143,7 +146,7 @@
     }
 
     private void openToReceiveCalls(SipProfile localProfile) {
-        open3(localProfile, SipManager.SIP_INCOMING_CALL_ACTION, null);
+        open3(localProfile, SipManager.ACTION_SIP_INCOMING_CALL, null);
     }
 
     public synchronized void open3(SipProfile localProfile,
@@ -172,7 +175,7 @@
         SipSessionGroupExt group = mSipGroups.remove(localProfileUri);
         if (group != null) {
             notifyProfileRemoved(group.getLocalProfile());
-            group.closeToNotReceiveCalls();
+            group.close();
             if (isWifiOn() && !anyOpened()) releaseWifiLock();
         }
     }
@@ -255,15 +258,15 @@
 
     private void notifyProfileAdded(SipProfile localProfile) {
         if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
-        Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION);
-        intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
+        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
         mContext.sendBroadcast(intent);
     }
 
     private void notifyProfileRemoved(SipProfile localProfile) {
         if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
-        Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION);
-        intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
+        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
         mContext.sendBroadcast(intent);
     }
 
@@ -449,9 +452,9 @@
             }
         }
 
-        public void closeToNotReceiveCalls() {
+        public void close() {
             mOpened = false;
-            mSipGroup.closeToNotReceiveCalls();
+            mSipGroup.close();
             mAutoRegistration.stop();
             if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
                     + mIncomingCallBroadcastAction);
@@ -474,8 +477,8 @@
                     // send out incoming call broadcast
                     addPendingSession(session);
                     Intent intent = SipManager.createIncomingCallBroadcast(
-                            mIncomingCallBroadcastAction, session.getCallId(),
-                            sessionDescription);
+                            session.getCallId(), sessionDescription)
+                            .setAction(mIncomingCallBroadcastAction);
                     if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
                             + caller.getUri() + ": " + session.getCallId()
                             + " " + mIncomingCallBroadcastAction);
@@ -613,10 +616,10 @@
 
                 try {
                     int state = (mSession == null)
-                            ? SipSessionState.READY_TO_CALL
+                            ? SipSession.State.READY_TO_CALL
                             : mSession.getState();
-                    if ((state == SipSessionState.REGISTERING)
-                            || (state == SipSessionState.DEREGISTERING)) {
+                    if ((state == SipSession.State.REGISTERING)
+                            || (state == SipSession.State.DEREGISTERING)) {
                         mProxy.onRegistering(mSession);
                     } else if (mRegistered) {
                         int duration = (int)
@@ -1138,7 +1141,8 @@
                 event.mTriggerTime += event.mPeriod;
 
                 // run the callback in a new thread to prevent deadlock
-                new Thread(event.mCallback).start();
+                new Thread(event.mCallback, "SipServiceTimerCallbackThread")
+                        .start();
             }
             if (DEBUG_TIMER) {
                 Log.d(TAG, "after timeout execution");
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
similarity index 88%
rename from services/java/com/android/server/sip/SipSessionGroup.java
rename to voip/java/com/android/server/sip/SipSessionGroup.java
index b4c2241..8f9a26b 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -25,11 +25,10 @@
 
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
-import android.net.sip.SessionDescription;
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipProfile;
+import android.net.sip.SipSession;
 import android.net.sip.SipSessionAdapter;
-import android.net.sip.SipSessionState;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -83,9 +82,8 @@
     private static final boolean DEBUG = true;
     private static final boolean DEBUG_PING = DEBUG && false;
     private static final String ANONYMOUS = "anonymous";
-    private static final String SERVER_ERROR_PREFIX = "Response: ";
     private static final int EXPIRY_TIME = 3600; // in seconds
-    private static final int CANCEL_CALL_TIMER = 5; // in seconds
+    private static final int CANCEL_CALL_TIMER = 3; // in seconds
 
     private static final EventObject DEREGISTER = new EventObject("Deregister");
     private static final EventObject END_CALL = new EventObject("End call");
@@ -121,7 +119,7 @@
         reset(localIp);
     }
 
-    void reset(String localIp) throws SipException, IOException {
+    synchronized void reset(String localIp) throws SipException, IOException {
         mLocalIp = localIp;
         if (localIp == null) return;
 
@@ -301,7 +299,7 @@
             boolean processed = (session != null) && session.process(event);
             if (isLoggable && processed) {
                 Log.d(TAG, "new state after: "
-                        + SipSessionState.toString(session.mState));
+                        + SipSession.State.toString(session.mState));
             }
         } catch (Throwable e) {
             Log.w(TAG, "event process error: " + event, e);
@@ -332,7 +330,7 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + SipSessionState.toString(mState) + ": processing "
+                    + SipSession.State.toString(mState) + ": processing "
                     + log(evt));
             if (isRequestEvent(Request.INVITE, evt)) {
                 RequestEvent event = (RequestEvent) evt;
@@ -342,13 +340,16 @@
                 newSession.mDialog = newSession.mServerTransaction.getDialog();
                 newSession.mInviteReceived = event;
                 newSession.mPeerProfile = createPeerProfile(event.getRequest());
-                newSession.mState = SipSessionState.INCOMING_CALL;
+                newSession.mState = SipSession.State.INCOMING_CALL;
                 newSession.mPeerSessionDescription =
                         extractContent(event.getRequest());
                 addSipSession(newSession);
                 mProxy.onRinging(newSession, newSession.mPeerProfile,
                         newSession.mPeerSessionDescription);
                 return true;
+            } else if (isRequestEvent(Request.OPTIONS, evt)) {
+                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
+                return true;
             } else {
                 return false;
             }
@@ -358,7 +359,7 @@
     class SipSessionImpl extends ISipSession.Stub {
         SipProfile mPeerProfile;
         SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
-        int mState = SipSessionState.READY_TO_CALL;
+        int mState = SipSession.State.READY_TO_CALL;
         RequestEvent mInviteReceived;
         Dialog mDialog;
         ServerTransaction mServerTransaction;
@@ -378,7 +379,7 @@
                         sleep(timeout);
                         if (mRunning) timeout();
                     }
-                }).start();
+                }, "SipSessionTimerThread").start();
             }
 
             synchronized void cancel() {
@@ -413,7 +414,7 @@
             mInCall = false;
             removeSipSession(this);
             mPeerProfile = null;
-            mState = SipSessionState.READY_TO_CALL;
+            mState = SipSession.State.READY_TO_CALL;
             mInviteReceived = null;
             mDialog = null;
             mServerTransaction = null;
@@ -470,7 +471,7 @@
                             onError(e);
                         }
                     }
-            }).start();
+            }, "SipSessionAsyncCmdThread").start();
         }
 
         public void makeCall(SipProfile peerProfile, String sessionDescription,
@@ -520,10 +521,10 @@
         }
 
         public void sendKeepAlive() {
-            mState = SipSessionState.PINGING;
+            mState = SipSession.State.PINGING;
             try {
                 processCommand(new OptionsCommand());
-                while (SipSessionState.PINGING == mState) {
+                while (SipSession.State.PINGING == mState) {
                     Thread.sleep(1000);
                 }
             } catch (SipException e) {
@@ -550,7 +551,7 @@
             try {
                 String s = super.toString();
                 return s.substring(s.indexOf("@")) + ":"
-                        + SipSessionState.toString(mState);
+                        + SipSession.State.toString(mState);
             } catch (Throwable e) {
                 return super.toString();
             }
@@ -558,7 +559,7 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + SipSessionState.toString(mState) + ": processing "
+                    + SipSession.State.toString(mState) + ": processing "
                     + log(evt));
             synchronized (SipSessionGroup.this) {
                 if (isClosed()) return false;
@@ -574,30 +575,30 @@
                 boolean processed;
 
                 switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     processed = registeringToReady(evt);
                     break;
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     processed = keepAliveProcess(evt);
                     break;
-                case SipSessionState.READY_TO_CALL:
+                case SipSession.State.READY_TO_CALL:
                     processed = readyForCall(evt);
                     break;
-                case SipSessionState.INCOMING_CALL:
+                case SipSession.State.INCOMING_CALL:
                     processed = incomingCall(evt);
                     break;
-                case SipSessionState.INCOMING_CALL_ANSWERING:
+                case SipSession.State.INCOMING_CALL_ANSWERING:
                     processed = incomingCallToInCall(evt);
                     break;
-                case SipSessionState.OUTGOING_CALL:
-                case SipSessionState.OUTGOING_CALL_RING_BACK:
+                case SipSession.State.OUTGOING_CALL:
+                case SipSession.State.OUTGOING_CALL_RING_BACK:
                     processed = outgoingCall(evt);
                     break;
-                case SipSessionState.OUTGOING_CALL_CANCELING:
+                case SipSession.State.OUTGOING_CALL_CANCELING:
                     processed = outgoingCallToReady(evt);
                     break;
-                case SipSessionState.IN_CALL:
+                case SipSession.State.IN_CALL:
                     processed = inCall(evt);
                     break;
                 default:
@@ -618,12 +619,17 @@
                         Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
                 return true;
             } else if (evt instanceof TransactionTerminatedEvent) {
-                if (evt instanceof TimeoutEvent) {
-                    processTimeout((TimeoutEvent) evt);
-                } else {
-                    processTransactionTerminated(
-                            (TransactionTerminatedEvent) evt);
+                if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
+                    if (evt instanceof TimeoutEvent) {
+                        processTimeout((TimeoutEvent) evt);
+                    } else {
+                        processTransactionTerminated(
+                                (TransactionTerminatedEvent) evt);
+                    }
+                    return true;
                 }
+            } else if (isRequestEvent(Request.OPTIONS, evt)) {
+                mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
                 return true;
             } else if (evt instanceof DialogTerminatedEvent) {
                 processDialogTerminated((DialogTerminatedEvent) evt);
@@ -641,11 +647,42 @@
             }
         }
 
+        private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
+            Transaction current = event.isServerTransaction()
+                    ? mServerTransaction
+                    : mClientTransaction;
+            Transaction target = event.isServerTransaction()
+                    ? event.getServerTransaction()
+                    : event.getClientTransaction();
+
+            if ((current != target) && (mState != SipSession.State.PINGING)) {
+                Log.d(TAG, "not the current transaction; current="
+                        + toString(current) + ", target=" + toString(target));
+                return false;
+            } else if (current != null) {
+                Log.d(TAG, "transaction terminated: " + toString(current));
+                return true;
+            } else {
+                // no transaction; shouldn't be here; ignored
+                return true;
+            }
+        }
+
+        private String toString(Transaction transaction) {
+            if (transaction == null) return "null";
+            Request request = transaction.getRequest();
+            Dialog dialog = transaction.getDialog();
+            CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
+            return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
+                    cseq.getSeqNumber(), transaction.getState(),
+                    ((dialog == null) ? "-" : dialog.getState()));
+        }
+
         private void processTransactionTerminated(
                 TransactionTerminatedEvent event) {
             switch (mState) {
-                case SipSessionState.IN_CALL:
-                case SipSessionState.READY_TO_CALL:
+                case SipSession.State.IN_CALL:
+                case SipSession.State.READY_TO_CALL:
                     Log.d(TAG, "Transaction terminated; do nothing");
                     break;
                 default:
@@ -656,35 +693,23 @@
         }
 
         private void processTimeout(TimeoutEvent event) {
-            Log.d(TAG, "processing Timeout..." + event);
-            Transaction current = event.isServerTransaction()
-                    ? mServerTransaction
-                    : mClientTransaction;
-            Transaction target = event.isServerTransaction()
-                    ? event.getServerTransaction()
-                    : event.getClientTransaction();
-
-            if ((current != target) && (mState != SipSessionState.PINGING)) {
-                Log.d(TAG, "not the current transaction; current=" + current
-                        + ", timed out=" + target);
-                return;
-            }
+            Log.d(TAG, "processing Timeout...");
             switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     reset();
                     mProxy.onRegistrationTimeout(this);
                     break;
-                case SipSessionState.INCOMING_CALL:
-                case SipSessionState.INCOMING_CALL_ANSWERING:
-                case SipSessionState.OUTGOING_CALL:
-                case SipSessionState.OUTGOING_CALL_CANCELING:
+                case SipSession.State.INCOMING_CALL:
+                case SipSession.State.INCOMING_CALL_ANSWERING:
+                case SipSession.State.OUTGOING_CALL:
+                case SipSession.State.OUTGOING_CALL_CANCELING:
                     onError(SipErrorCode.TIME_OUT, event.toString());
                     break;
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     reset();
                     mReRegisterFlag = true;
-                    mState = SipSessionState.READY_TO_CALL;
+                    mState = SipSession.State.READY_TO_CALL;
                     break;
 
                 default:
@@ -758,7 +783,7 @@
                 switch (statusCode) {
                 case Response.OK:
                     int state = mState;
-                    onRegistrationDone((state == SipSessionState.REGISTERING)
+                    onRegistrationDone((state == SipSession.State.REGISTERING)
                             ? getExpiryTime(((ResponseEvent) evt).getResponse())
                             : -1);
                     mLastNonce = null;
@@ -805,6 +830,12 @@
             }
         }
 
+        private boolean crossDomainAuthenticationRequired(Response response) {
+            String realm = getRealmFromResponse(response);
+            if (realm == null) realm = "";
+            return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
+        }
+
         private AccountManager getAccountManager() {
             return new AccountManager() {
                 public UserCredentials getCredentials(ClientTransaction
@@ -826,6 +857,15 @@
             };
         }
 
+        private String getRealmFromResponse(Response response) {
+            WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
+                    SIPHeaderNames.WWW_AUTHENTICATE);
+            if (wwwAuth != null) return wwwAuth.getRealm();
+            ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
+                    SIPHeaderNames.PROXY_AUTHENTICATE);
+            return (proxyAuth == null) ? null : proxyAuth.getRealm();
+        }
+
         private String getNonceFromResponse(Response response) {
             WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
                     SIPHeaderNames.WWW_AUTHENTICATE);
@@ -845,7 +885,7 @@
                         generateTag());
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.OUTGOING_CALL;
+                mState = SipSession.State.OUTGOING_CALL;
                 mProxy.onCalling(this);
                 startSessionTimer(cmd.getTimeout());
                 return true;
@@ -855,7 +895,7 @@
                         generateTag(), duration);
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.REGISTERING;
+                mState = SipSession.State.REGISTERING;
                 mProxy.onRegistering(this);
                 return true;
             } else if (DEREGISTER == evt) {
@@ -863,7 +903,7 @@
                         generateTag(), 0);
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.DEREGISTERING;
+                mState = SipSession.State.DEREGISTERING;
                 mProxy.onRegistering(this);
                 return true;
             }
@@ -878,7 +918,7 @@
                         mLocalProfile,
                         ((MakeCallCommand) evt).getSessionDescription(),
                         mServerTransaction);
-                mState = SipSessionState.INCOMING_CALL_ANSWERING;
+                mState = SipSession.State.INCOMING_CALL_ANSWERING;
                 startSessionTimer(((MakeCallCommand) evt).getTimeout());
                 return true;
             } else if (END_CALL == evt) {
@@ -919,8 +959,8 @@
                 int statusCode = response.getStatusCode();
                 switch (statusCode) {
                 case Response.RINGING:
-                    if (mState == SipSessionState.OUTGOING_CALL) {
-                        mState = SipSessionState.OUTGOING_CALL_RING_BACK;
+                    if (mState == SipSession.State.OUTGOING_CALL) {
+                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
                         mProxy.onRingingBack(this);
                         cancelSessionTimer();
                     }
@@ -932,7 +972,10 @@
                     return true;
                 case Response.UNAUTHORIZED:
                 case Response.PROXY_AUTHENTICATION_REQUIRED:
-                    if (handleAuthentication(event)) {
+                    if (crossDomainAuthenticationRequired(response)) {
+                        onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
+                                getRealmFromResponse(response));
+                    } else if (handleAuthentication(event)) {
                         addSipSession(this);
                     } else if (mLastNonce == null) {
                         onError(SipErrorCode.SERVER_ERROR,
@@ -963,7 +1006,7 @@
                 // response comes back yet. We are cheating for not checking
                 // response.
                 mSipHelper.sendCancel(mClientTransaction);
-                mState = SipSessionState.OUTGOING_CALL_CANCELING;
+                mState = SipSession.State.OUTGOING_CALL_CANCELING;
                 startSessionTimer(CANCEL_CALL_TIMER);
                 return true;
             }
@@ -977,19 +1020,19 @@
                 Response response = event.getResponse();
                 int statusCode = response.getStatusCode();
                 if (expectResponse(Request.CANCEL, evt)) {
+                    if (statusCode == Response.OK) {
+                        // do nothing; wait for REQUEST_TERMINATED
+                        return true;
+                    }
+                } else if (expectResponse(Request.INVITE, evt)) {
                     switch (statusCode) {
                         case Response.OK:
-                            // do nothing; wait for REQUEST_TERMINATED
+                            outgoingCall(evt); // abort Cancel
                             return true;
                         case Response.REQUEST_TERMINATED:
                             endCallNormally();
                             return true;
                     }
-                } else if (expectResponse(Request.INVITE, evt)) {
-                    if (statusCode == Response.OK) {
-                        outgoingCall(evt); // abort Cancel
-                        return true;
-                    }
                 } else {
                     return false;
                 }
@@ -1019,7 +1062,7 @@
             } else if (isRequestEvent(Request.INVITE, evt)) {
                 // got Re-INVITE
                 RequestEvent event = mInviteReceived = (RequestEvent) evt;
-                mState = SipSessionState.INCOMING_CALL;
+                mState = SipSession.State.INCOMING_CALL;
                 mPeerSessionDescription = extractContent(event.getRequest());
                 mServerTransaction = null;
                 mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
@@ -1032,7 +1075,7 @@
                 // to change call
                 mClientTransaction = mSipHelper.sendReinvite(mDialog,
                         ((MakeCallCommand) evt).getSessionDescription());
-                mState = SipSessionState.OUTGOING_CALL;
+                mState = SipSession.State.OUTGOING_CALL;
                 startSessionTimer(((MakeCallCommand) evt).getTimeout());
                 return true;
             }
@@ -1055,19 +1098,19 @@
         }
 
         private String createErrorMessage(Response response) {
-            return String.format(SERVER_ERROR_PREFIX + "%s (%d)",
-                    response.getReasonPhrase(), response.getStatusCode());
+            return String.format("%s (%d)", response.getReasonPhrase(),
+                    response.getStatusCode());
         }
 
         private void establishCall() {
-            mState = SipSessionState.IN_CALL;
+            mState = SipSession.State.IN_CALL;
             mInCall = true;
             cancelSessionTimer();
             mProxy.onCallEstablished(this, mPeerSessionDescription);
         }
 
         private void fallbackToPreviousInCall(int errorCode, String message) {
-            mState = SipSessionState.IN_CALL;
+            mState = SipSession.State.IN_CALL;
             mProxy.onCallChangeFailed(this, errorCode, message);
         }
 
@@ -1089,8 +1132,8 @@
         private void onError(int errorCode, String message) {
             cancelSessionTimer();
             switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     onRegistrationFailed(errorCode, message);
                     break;
                 default:
@@ -1160,8 +1203,6 @@
                 return SipErrorCode.INVALID_REMOTE_URI;
             } else if (exception instanceof IOException) {
                 return SipErrorCode.SOCKET_ERROR;
-            } else if (message.startsWith(SERVER_ERROR_PREFIX)) {
-                return SipErrorCode.SERVER_ERROR;
             } else {
                 return SipErrorCode.CLIENT_ERROR;
             }
@@ -1264,7 +1305,7 @@
     private static boolean isLoggable(SipSessionImpl s) {
         if (s != null) {
             switch (s.mState) {
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     return DEBUG_PING;
             }
         }
diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/voip/java/com/android/server/sip/SipSessionListenerProxy.java
similarity index 98%
rename from services/java/com/android/server/sip/SipSessionListenerProxy.java
rename to voip/java/com/android/server/sip/SipSessionListenerProxy.java
index a4cd102..f8be0a8 100644
--- a/services/java/com/android/server/sip/SipSessionListenerProxy.java
+++ b/voip/java/com/android/server/sip/SipSessionListenerProxy.java
@@ -40,7 +40,7 @@
         // One thread for each calling back.
         // Note: Guarantee ordering if the issue becomes important. Currently,
         // the chance of handling two callback events at a time is none.
-        new Thread(runnable).start();
+        new Thread(runnable, "SipSessionCallbackThread").start();
     }
 
     public void onCalling(final ISipSession session) {
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
index ddd07fc..4d8d36c 100644
--- a/voip/jni/rtp/AudioCodec.cpp
+++ b/voip/jni/rtp/AudioCodec.cpp
@@ -36,9 +36,9 @@
 class UlawCodec : public AudioCodec
 {
 public:
-    bool set(int sampleRate, int sampleCount) {
-        mSampleCount = sampleCount;
-        return sampleCount > 0;
+    int set(int sampleRate, const char *fmtp) {
+        mSampleCount = sampleRate / 50;
+        return mSampleCount;
     }
     int encode(void *payload, int16_t *samples);
     int decode(int16_t *samples, void *payload, int length);
@@ -89,9 +89,9 @@
 class AlawCodec : public AudioCodec
 {
 public:
-    bool set(int sampleRate, int sampleCount) {
-        mSampleCount = sampleCount;
-        return sampleCount > 0;
+    int set(int sampleRate, const char *fmtp) {
+        mSampleCount = sampleRate / 50;
+        return mSampleCount;
     }
     int encode(void *payload, int16_t *samples);
     int decode(int16_t *samples, void *payload, int length);
@@ -152,8 +152,10 @@
 {
     AudioCodecType *type = gAudioCodecTypes;
     while (type->name != NULL) {
-        if (strcmp(codecName, type->name) == 0) {
-            return type->create();
+        if (strcasecmp(codecName, type->name) == 0) {
+            AudioCodec *codec = type->create();
+            codec->name = type->name;
+            return codec;
         }
         ++type;
     }
diff --git a/voip/jni/rtp/AudioCodec.h b/voip/jni/rtp/AudioCodec.h
index 797494c..e389255 100644
--- a/voip/jni/rtp/AudioCodec.h
+++ b/voip/jni/rtp/AudioCodec.h
@@ -22,9 +22,11 @@
 class AudioCodec
 {
 public:
+    const char *name;
+    // Needed by destruction through base class pointers.
     virtual ~AudioCodec() {}
-    // Returns true if initialization succeeds.
-    virtual bool set(int sampleRate, int sampleCount) = 0;
+    // Returns sampleCount or non-positive value if unsupported.
+    virtual int set(int sampleRate, const char *fmtp) = 0;
     // Returns the length of payload in bytes.
     virtual int encode(void *payload, int16_t *samples) = 0;
     // Returns the number of decoded samples.
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 3433dcf..81d4dfc 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -77,7 +77,7 @@
     AudioStream();
     ~AudioStream();
     bool set(int mode, int socket, sockaddr_storage *remote,
-        const char *codecName, int sampleRate, int sampleCount,
+        AudioCodec *codec, int sampleRate, int sampleCount,
         int codecType, int dtmfType);
 
     void sendDtmf(int event);
@@ -86,6 +86,8 @@
     void decode(int tick);
 
 private:
+    bool isNatAddress(struct sockaddr_storage *addr);
+
     enum {
         NORMAL = 0,
         SEND_ONLY = 1,
@@ -104,6 +106,7 @@
     int mSampleRate;
     int mSampleCount;
     int mInterval;
+    int mLogThrottle;
 
     int16_t *mBuffer;
     int mBufferMask;
@@ -140,7 +143,7 @@
 }
 
 bool AudioStream::set(int mode, int socket, sockaddr_storage *remote,
-    const char *codecName, int sampleRate, int sampleCount,
+    AudioCodec *codec, int sampleRate, int sampleCount,
     int codecType, int dtmfType)
 {
     if (mode < 0 || mode > LAST_MODE) {
@@ -148,14 +151,6 @@
     }
     mMode = mode;
 
-    if (codecName) {
-        mRemote = *remote;
-        mCodec = newAudioCodec(codecName);
-        if (!mCodec || !mCodec->set(sampleRate, sampleCount)) {
-            return false;
-        }
-    }
-
     mCodecMagic = (0x8000 | codecType) << 16;
     mDtmfMagic = (dtmfType == -1) ? 0 : (0x8000 | dtmfType) << 16;
 
@@ -181,11 +176,15 @@
     mDtmfEvent = -1;
     mDtmfStart = 0;
 
-    // Only take over the socket when succeeded.
+    // Only take over these things when succeeded.
     mSocket = socket;
+    if (codec) {
+        mRemote = *remote;
+        mCodec = codec;
+    }
 
     LOGD("stream[%d] is configured as %s %dkHz %dms", mSocket,
-        (codecName ? codecName : "RAW"), mSampleRate, mInterval);
+        (codec ? codec->name : "RAW"), mSampleRate, mInterval);
     return true;
 }
 
@@ -282,7 +281,10 @@
         chain = chain->mNext;
     }
     if (!mixed) {
-        LOGD("stream[%d] no data", mSocket);
+        if ((mTick ^ mLogThrottle) >> 10) {
+            mLogThrottle = mTick;
+            LOGD("stream[%d] no data", mSocket);
+        }
         return;
     }
 
@@ -316,6 +318,16 @@
         sizeof(mRemote));
 }
 
+bool AudioStream::isNatAddress(struct sockaddr_storage *addr) {
+    if (addr->ss_family != AF_INET) return false;
+    struct sockaddr_in *s4 = (struct sockaddr_in *)addr;
+    unsigned char *d = (unsigned char *) &s4->sin_addr;
+    if ((d[0] == 10)
+        || ((d[0] == 172) && (d[1] & 0x10))
+        || ((d[0] == 192) && (d[1] == 168))) return true;
+    return false;
+}
+
 void AudioStream::decode(int tick)
 {
     char c;
@@ -363,8 +375,21 @@
             MSG_TRUNC | MSG_DONTWAIT) >> 1;
     } else {
         __attribute__((aligned(4))) uint8_t buffer[2048];
-        length = recv(mSocket, buffer, sizeof(buffer),
-            MSG_TRUNC | MSG_DONTWAIT);
+        struct sockaddr_storage src_addr;
+        socklen_t addrlen;
+        length = recvfrom(mSocket, buffer, sizeof(buffer),
+            MSG_TRUNC|MSG_DONTWAIT, (sockaddr*)&src_addr, &addrlen);
+
+        // The following if clause is for fixing the target address if
+        // proxy server did not replace the NAT address with its media
+        // port in SDP. Although it is proxy server's responsibility for
+        // replacing the connection address with correct one, we will change
+        // the target address as we detect the difference for now until we
+        // know the best way to get rid of this issue.
+        if ((memcmp((void*)&src_addr, (void*)&mRemote, addrlen) != 0) &&
+            isNatAddress(&mRemote)) {
+            memcpy((void*)&mRemote, (void*)&src_addr, addrlen);
+        }
 
         // Do we need to check SSRC, sequence, and timestamp? They are not
         // reliable but at least they can be used to identify duplicates?
@@ -831,10 +856,9 @@
 
 void add(JNIEnv *env, jobject thiz, jint mode,
     jint socket, jstring jRemoteAddress, jint remotePort,
-    jstring jCodecName, jint sampleRate, jint sampleCount,
-    jint codecType, jint dtmfType)
+    jstring jCodecSpec, jint dtmfType)
 {
-    const char *codecName = NULL;
+    AudioCodec *codec = NULL;
     AudioStream *stream = NULL;
     AudioGroup *group = NULL;
 
@@ -842,33 +866,42 @@
     sockaddr_storage remote;
     if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
         // Exception already thrown.
-        goto error;
+        return;
     }
-    if (sampleRate < 0 || sampleCount < 0 || codecType < 0 || codecType > 127) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        goto error;
+    if (!jCodecSpec) {
+        jniThrowNullPointerException(env, "codecSpec");
+        return;
     }
-    if (!jCodecName) {
-        jniThrowNullPointerException(env, "codecName");
-        goto error;
-    }
-    codecName = env->GetStringUTFChars(jCodecName, NULL);
-    if (!codecName) {
+    const char *codecSpec = env->GetStringUTFChars(jCodecSpec, NULL);
+    if (!codecSpec) {
         // Exception already thrown.
+        return;
+    }
+
+    // Create audio codec.
+    int codecType = -1;
+    char codecName[16];
+    int sampleRate = -1;
+    sscanf(codecSpec, "%d %[^/]%*c%d", &codecType, codecName, &sampleRate);
+    codec = newAudioCodec(codecName);
+    int sampleCount = (codec ? codec->set(sampleRate, codecSpec) : -1);
+    env->ReleaseStringUTFChars(jCodecSpec, codecSpec);
+    if (sampleCount <= 0) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "cannot initialize audio codec");
         goto error;
     }
 
     // Create audio stream.
     stream = new AudioStream;
-    if (!stream->set(mode, socket, &remote, codecName, sampleRate, sampleCount,
+    if (!stream->set(mode, socket, &remote, codec, sampleRate, sampleCount,
         codecType, dtmfType)) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "cannot initialize audio stream");
-        env->ReleaseStringUTFChars(jCodecName, codecName);
         goto error;
     }
-    env->ReleaseStringUTFChars(jCodecName, codecName);
     socket = -1;
+    codec = NULL;
 
     // Create audio group.
     group = (AudioGroup *)env->GetIntField(thiz, gNative);
@@ -896,6 +929,7 @@
 error:
     delete group;
     delete stream;
+    delete codec;
     close(socket);
     env->SetIntField(thiz, gNative, NULL);
 }
@@ -930,7 +964,7 @@
 }
 
 JNINativeMethod gMethods[] = {
-    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)V", (void *)add},
+    {"add", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add},
     {"remove", "(I)V", (void *)remove},
     {"setMode", "(I)V", (void *)setMode},
     {"sendDtmf", "(I)V", (void *)sendDtmf},
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0ee559e..198b1e6 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -42,7 +42,7 @@
 
     boolean pingSupplicant();
 
-    boolean startScan(boolean forceActive);
+    void startScan(boolean forceActive);
 
     List<ScanResult> getScanResults();
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index dd162f2..f883588 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -570,7 +570,8 @@
      */
     public boolean startScan() {
         try {
-            return mService.startScan(false);
+            mService.startScan(false);
+            return true;
         } catch (RemoteException e) {
             return false;
         }
@@ -588,7 +589,8 @@
      */
     public boolean startScanActive() {
         try {
-            return mService.startScan(true);
+            mService.startScan(true);
+            return true;
         } catch (RemoteException e) {
             return false;
         }