Merge "Add display lists caching."
diff --git a/Android.mk b/Android.mk
index 9492167..570fbf8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -161,6 +161,14 @@
 	core/java/com/android/internal/view/IInputMethodManager.aidl \
 	core/java/com/android/internal/view/IInputMethodSession.aidl \
 	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
+	core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl \
+	core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl \
+	core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl \
+	core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl \
+	core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl \
+	core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl \
+	core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl \
+	core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl \
 	location/java/android/location/ICountryDetector.aidl \
 	location/java/android/location/ICountryListener.aidl \
 	location/java/android/location/IGeocodeProvider.aidl \
diff --git a/api/current.xml b/api/current.xml
index 42787e6..e3b6a01 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -6829,6 +6829,17 @@
  visibility="public"
 >
 </field>
+<field name="listDividerAlertDialog"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843590"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="listPopupWindowStyle"
  type="int"
  transient="false"
@@ -9876,6 +9887,17 @@
  visibility="public"
 >
 </field>
+<field name="textColorAlertDialogListItem"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843591"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textColorHighlight"
  type="int"
  transient="false"
@@ -26663,6 +26685,17 @@
  visibility="public"
 >
 </method>
+<method name="getContext"
+ return="android.content.Context"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="setAdapter"
  return="android.app.AlertDialog.Builder"
  abstract="false"
@@ -28621,6 +28654,623 @@
 >
 </field>
 </class>
+<class name="DownloadManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="enqueue"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="request" type="android.app.DownloadManager.Request">
+</parameter>
+</method>
+<method name="openDownloadedFile"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
+<method name="query"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="query" type="android.app.DownloadManager.Query">
+</parameter>
+</method>
+<method name="remove"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+</method>
+<field name="ACTION_DOWNLOAD_COMPLETE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.DOWNLOAD_COMPLETE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_NOTIFICATION_CLICKED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_VIEW_DOWNLOADS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.VIEW_DOWNLOADS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;bytes_so_far&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_DESCRIPTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;description&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_ERROR_CODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;error_code&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_LAST_MODIFIED_TIMESTAMP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;last_modified_timestamp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_LOCAL_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;local_uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_MEDIA_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;media_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_STATUS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;status&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_TITLE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;title&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_TOTAL_SIZE_BYTES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;total_size&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CANNOT_RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1008"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_DEVICE_NOT_FOUND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1007"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FILE_ALREADY_EXISTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1009"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FILE_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1001"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_HTTP_DATA_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1004"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_INSUFFICIENT_SPACE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1006"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_TOO_MANY_REDIRECTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1005"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNHANDLED_HTTP_CODE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1002"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DOWNLOAD_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;extra_download_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_FAILED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_PAUSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_PENDING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_RUNNING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_SUCCESSFUL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DownloadManager.Query"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DownloadManager.Query"
+ type="android.app.DownloadManager.Query"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="setFilterById"
+ return="android.app.DownloadManager.Query"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+</method>
+<method name="setFilterByStatus"
+ return="android.app.DownloadManager.Query"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+</class>
+<class name="DownloadManager.Request"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DownloadManager.Request"
+ type="android.app.DownloadManager.Request"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<method name="addRequestHeader"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="setAllowedNetworkTypes"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="setAllowedOverRoaming"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="allowed" type="boolean">
+</parameter>
+</method>
+<method name="setDescription"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="description" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setDestinationInExternalFilesDir"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="dirType" type="java.lang.String">
+</parameter>
+<parameter name="subPath" type="java.lang.String">
+</parameter>
+</method>
+<method name="setDestinationInExternalPublicDir"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dirType" type="java.lang.String">
+</parameter>
+<parameter name="subPath" type="java.lang.String">
+</parameter>
+</method>
+<method name="setDestinationUri"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="setMimeType"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="setShowRunningNotification"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="show" type="boolean">
+</parameter>
+</method>
+<method name="setTitle"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setVisibleInDownloadsUi"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isVisible" type="boolean">
+</parameter>
+</method>
+<field name="NETWORK_MOBILE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NETWORK_WIFI"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ExpandableListActivity"
  extends="android.app.Activity"
  abstract="false"
@@ -38025,6 +38675,111 @@
 </package>
 <package name="android.bluetooth"
 >
+<class name="BluetoothA2dp"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.bluetooth.BluetoothProfile">
+</implements>
+<method name="getConnectedDevices"
+ return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getConnectionState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="getDevicesMatchingConnectionStates"
+ return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int[]">
+</parameter>
+</method>
+<method name="isA2dpPlaying"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<field name="ACTION_CONNECTION_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PLAYING_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_NOT_PLAYING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_PLAYING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="BluetoothAdapter"
  extends="java.lang.Object"
  abstract="false"
@@ -38057,6 +38812,21 @@
 <parameter name="address" type="java.lang.String">
 </parameter>
 </method>
+<method name="closeProfileProxy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="profile" type="int">
+</parameter>
+<parameter name="proxy" type="android.bluetooth.BluetoothProfile">
+</parameter>
+</method>
 <method name="disable"
  return="boolean"
  abstract="false"
@@ -38123,6 +38893,23 @@
  visibility="public"
 >
 </method>
+<method name="getProfileProxy"
+ return="boolean"
+ 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="listener" type="android.bluetooth.BluetoothProfile.ServiceListener">
+</parameter>
+<parameter name="profile" type="int">
+</parameter>
+</method>
 <method name="getRemoteDevice"
  return="android.bluetooth.BluetoothDevice"
  abstract="false"
@@ -38221,6 +39008,17 @@
  visibility="public"
 >
 </method>
+<field name="ACTION_CONNECTION_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_DISCOVERY_FINISHED"
  type="java.lang.String"
  transient="false"
@@ -38309,6 +39107,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_CONNECTION_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.adapter.extra.CONNECTION_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_DISCOVERABLE_DURATION"
  type="java.lang.String"
  transient="false"
@@ -38331,6 +39140,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_PREVIOUS_CONNECTION_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_PREVIOUS_SCAN_MODE"
  type="java.lang.String"
  transient="false"
@@ -38408,6 +39228,50 @@
  visibility="public"
 >
 </field>
+<field name="STATE_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_CONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="STATE_OFF"
  type="int"
  transient="false"
@@ -39649,6 +40513,306 @@
 >
 </field>
 </class>
+<class name="BluetoothHeadset"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.bluetooth.BluetoothProfile">
+</implements>
+<method name="getConnectedDevices"
+ return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getConnectionState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="getDevicesMatchingConnectionStates"
+ return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int[]">
+</parameter>
+</method>
+<method name="isAudioConnected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="startVoiceRecognition"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="stopVoiceRecognition"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<field name="ACTION_AUDIO_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_CONNECTION_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_AUDIO_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_AUDIO_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="BluetoothProfile"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getConnectedDevices"
+ return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getConnectionState"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="getDevicesMatchingConnectionStates"
+ return="java.util.Set&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int[]">
+</parameter>
+</method>
+<field name="A2DP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PREVIOUS_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.profile.extra.PREVIOUS_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.profile.extra.STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="HEADSET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_CONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
+<interface name="BluetoothProfile.ServiceListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onServiceConnected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="profile" type="int">
+</parameter>
+<parameter name="proxy" type="android.bluetooth.BluetoothProfile">
+</parameter>
+</method>
+<method name="onServiceDisconnected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="profile" type="int">
+</parameter>
+</method>
+</interface>
 <class name="BluetoothServerSocket"
  extends="java.lang.Object"
  abstract="false"
@@ -56730,6 +57894,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"
@@ -59833,6 +61008,118 @@
 >
 </field>
 </class>
+<class name="ObbInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OBB_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="flags"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="packageName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="version"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ObbScanner"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getObbInfo"
+ return="android.content.res.ObbInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filePath" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
 <class name="Resources"
  extends="java.lang.Object"
  abstract="false"
@@ -66007,7 +67294,7 @@
  return="boolean"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -66589,19 +67876,6 @@
 <exception name="SQLException" type="android.database.SQLException">
 </exception>
 </method>
-<method name="setConnectionPoolSize"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="size" type="int">
-</parameter>
-</method>
 <method name="setLocale"
  return="void"
  abstract="false"
@@ -105876,623 +107150,6 @@
 >
 </field>
 </class>
-<class name="DownloadManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="enqueue"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="request" type="android.net.DownloadManager.Request">
-</parameter>
-</method>
-<method name="openDownloadedFile"
- return="android.os.ParcelFileDescriptor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="long">
-</parameter>
-<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
-</exception>
-</method>
-<method name="query"
- return="android.database.Cursor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="query" type="android.net.DownloadManager.Query">
-</parameter>
-</method>
-<method name="remove"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="long">
-</parameter>
-</method>
-<field name="ACTION_DOWNLOAD_COMPLETE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.DOWNLOAD_COMPLETE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_NOTIFICATION_CLICKED"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_VIEW_DOWNLOADS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.VIEW_DOWNLOADS&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;bytes_so_far&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_DESCRIPTION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;description&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_ERROR_CODE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;error_code&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;_id&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_LAST_MODIFIED_TIMESTAMP"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;last_modified_timestamp&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_LOCAL_URI"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;local_uri&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_MEDIA_TYPE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;media_type&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_STATUS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;status&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_TITLE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;title&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_TOTAL_SIZE_BYTES"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;total_size&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_URI"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;uri&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_CANNOT_RESUME"
- type="int"
- transient="false"
- volatile="false"
- value="1008"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_DEVICE_NOT_FOUND"
- type="int"
- transient="false"
- volatile="false"
- value="1007"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_FILE_ALREADY_EXISTS"
- type="int"
- transient="false"
- volatile="false"
- value="1009"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_FILE_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="1001"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_HTTP_DATA_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="1004"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INSUFFICIENT_SPACE"
- type="int"
- transient="false"
- volatile="false"
- value="1006"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_TOO_MANY_REDIRECTS"
- type="int"
- transient="false"
- volatile="false"
- value="1005"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_UNHANDLED_HTTP_CODE"
- type="int"
- transient="false"
- volatile="false"
- value="1002"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_UNKNOWN"
- type="int"
- transient="false"
- volatile="false"
- value="1000"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_DOWNLOAD_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;extra_download_id&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_FAILED"
- type="int"
- transient="false"
- volatile="false"
- value="16"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_PAUSED"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_PENDING"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_RUNNING"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_SUCCESSFUL"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="DownloadManager.Query"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="DownloadManager.Query"
- type="android.net.DownloadManager.Query"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="setFilterById"
- return="android.net.DownloadManager.Query"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="long">
-</parameter>
-</method>
-<method name="setFilterByStatus"
- return="android.net.DownloadManager.Query"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
-</class>
-<class name="DownloadManager.Request"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="DownloadManager.Request"
- type="android.net.DownloadManager.Request"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</constructor>
-<method name="addRequestHeader"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="header" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.lang.String">
-</parameter>
-</method>
-<method name="setAllowedNetworkTypes"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<method name="setAllowedOverRoaming"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="allowed" type="boolean">
-</parameter>
-</method>
-<method name="setDescription"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="description" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setDestinationInExternalFilesDir"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="dirType" type="java.lang.String">
-</parameter>
-<parameter name="subPath" type="java.lang.String">
-</parameter>
-</method>
-<method name="setDestinationInExternalPublicDir"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dirType" type="java.lang.String">
-</parameter>
-<parameter name="subPath" type="java.lang.String">
-</parameter>
-</method>
-<method name="setDestinationUri"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</method>
-<method name="setMimeType"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="mimeType" type="java.lang.String">
-</parameter>
-</method>
-<method name="setShowRunningNotification"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="show" type="boolean">
-</parameter>
-</method>
-<method name="setTitle"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="title" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setVisibleInDownloadsUi"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="isVisible" type="boolean">
-</parameter>
-</method>
-<field name="NETWORK_MOBILE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NETWORK_WIFI"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="LocalServerSocket"
  extends="java.lang.Object"
  abstract="false"
@@ -139639,6 +140296,38 @@
 </package>
 <package name="android.os.storage"
 >
+<class name="OnObbStateChangeListener"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OnObbStateChangeListener"
+ type="android.os.storage.OnObbStateChangeListener"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onObbStateChange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="state" type="java.lang.String">
+</parameter>
+</method>
+</class>
 <class name="StorageEventListener"
  extends="java.lang.Object"
  abstract="true"
@@ -139780,6 +140469,8 @@
 </parameter>
 <parameter name="key" type="java.lang.String">
 </parameter>
+<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
+</parameter>
 </method>
 <method name="registerListener"
  return="void"
@@ -139808,6 +140499,8 @@
 </parameter>
 <parameter name="force" type="boolean">
 </parameter>
+<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
+</parameter>
 <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
 </exception>
 </method>
@@ -153459,6 +154152,21 @@
 <parameter name="volumeName" type="java.lang.String">
 </parameter>
 </method>
+<method name="getContentUriForAudioId"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="volumeName" type="java.lang.String">
+</parameter>
+<parameter name="audioId" type="int">
+</parameter>
+</method>
 <field name="CONTENT_TYPE"
  type="java.lang.String"
  transient="false"
@@ -155564,6 +156272,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"
@@ -240940,7 +241659,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
@@ -247861,7 +248580,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fileName" type="java.lang.String">
+<parameter name="path" type="java.lang.String">
 </parameter>
 <exception name="FileNotFoundException" type="java.io.FileNotFoundException">
 </exception>
@@ -247984,7 +248703,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="filename" type="java.lang.String">
+<parameter name="path" type="java.lang.String">
 </parameter>
 <exception name="FileNotFoundException" type="java.io.FileNotFoundException">
 </exception>
@@ -247996,7 +248715,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="filename" type="java.lang.String">
+<parameter name="path" type="java.lang.String">
 </parameter>
 <parameter name="append" type="boolean">
 </parameter>
@@ -248667,7 +249386,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="b" type="byte[]">
+<parameter name="buffer" type="byte[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
@@ -283744,9 +284463,9 @@
 >
 <parameter name="dst" type="byte[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -283990,9 +284709,9 @@
 >
 <parameter name="src" type="byte[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -284229,7 +284948,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </method>
 </class>
@@ -284462,9 +285181,9 @@
 >
 <parameter name="dst" type="char[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="charCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -284562,9 +285281,9 @@
 >
 <parameter name="src" type="char[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="charCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -284693,7 +285412,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="charCount" type="int">
 </parameter>
 </method>
 <method name="wrap"
@@ -284854,9 +285573,9 @@
 >
 <parameter name="dst" type="double[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="doubleCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -284943,9 +285662,9 @@
 >
 <parameter name="src" type="double[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="doubleCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -285014,7 +285733,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="doubleCount" type="int">
 </parameter>
 </method>
 </class>
@@ -285145,9 +285864,9 @@
 >
 <parameter name="dst" type="float[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="floatCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -285234,9 +285953,9 @@
 >
 <parameter name="src" type="float[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="floatCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -285305,7 +286024,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="floatCount" type="int">
 </parameter>
 </method>
 </class>
@@ -285436,9 +286155,9 @@
 >
 <parameter name="dst" type="int[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="intCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -285525,9 +286244,9 @@
 >
 <parameter name="src" type="int[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="intCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -285596,7 +286315,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="intCount" type="int">
 </parameter>
 </method>
 </class>
@@ -285744,9 +286463,9 @@
 >
 <parameter name="dst" type="long[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="longCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -285833,9 +286552,9 @@
 >
 <parameter name="src" type="long[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="longCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -285904,7 +286623,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="longCount" type="int">
 </parameter>
 </method>
 </class>
@@ -286094,9 +286813,9 @@
 >
 <parameter name="dst" type="short[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="shortCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -286183,9 +286902,9 @@
 >
 <parameter name="src" type="short[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="shortCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -286254,7 +286973,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="shortCount" type="int">
 </parameter>
 </method>
 </class>
@@ -305844,7 +306563,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.Blob">
+<parameter name="blob" type="java.sql.Blob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306003,7 +306722,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.Clob">
+<parameter name="clob" type="java.sql.Clob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306141,7 +306860,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="value" type="java.io.Reader">
+<parameter name="reader" type="java.io.Reader">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -306177,7 +306896,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="value" type="java.sql.NClob">
+<parameter name="nclob" type="java.sql.NClob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306230,7 +306949,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="value" type="java.lang.String">
+<parameter name="string" type="java.lang.String">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306340,7 +307059,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="rowId" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -306357,7 +307076,7 @@
 >
 <parameter name="parameterName" type="java.lang.String">
 </parameter>
-<parameter name="xmlObject" type="java.sql.SQLXML">
+<parameter name="sqlXml" type="java.sql.SQLXML">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -311493,7 +312212,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -311512,7 +312231,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -311565,7 +312284,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -311584,7 +312303,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.io.InputStream">
+<parameter name="inputStream" type="java.io.InputStream">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -311917,7 +312636,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="value" type="java.io.Reader">
+<parameter name="reader" type="java.io.Reader">
 </parameter>
 <parameter name="length" type="long">
 </parameter>
@@ -311936,7 +312655,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="value" type="java.io.Reader">
+<parameter name="reader" type="java.io.Reader">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -312006,7 +312725,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="value" type="java.lang.String">
+<parameter name="theString" type="java.lang.String">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -312133,7 +312852,7 @@
 >
 <parameter name="parameterIndex" type="int">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="theRowId" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -315127,7 +315846,7 @@
 >
 <parameter name="columnIndex" type="int">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="value" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -315144,7 +315863,7 @@
 >
 <parameter name="columnLabel" type="java.lang.String">
 </parameter>
-<parameter name="x" type="java.sql.RowId">
+<parameter name="value" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317476,7 +318195,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.sql.NClob">
+<parameter name="theNClob" type="java.sql.NClob">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317491,7 +318210,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.lang.String">
+<parameter name="theString" type="java.lang.String">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317536,7 +318255,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.sql.RowId">
+<parameter name="theRowId" type="java.sql.RowId">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
@@ -317551,7 +318270,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="x" type="java.sql.SQLXML">
+<parameter name="theXml" type="java.sql.SQLXML">
 </parameter>
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
diff --git a/cmds/screencap/Android.mk b/cmds/screencap/Android.mk
new file mode 100644
index 0000000..1a6e23e
--- /dev/null
+++ b/cmds/screencap/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	screencap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	libbinder \
+    libui \
+    libsurfaceflinger_client
+
+LOCAL_MODULE:= screencap
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
new file mode 100644
index 0000000..6ce5b86
--- /dev/null
+++ b/cmds/screencap/screencap.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <binder/IMemory.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+    const String16 name("SurfaceFlinger");
+    sp<ISurfaceComposer> composer;
+    if (getService(name, &composer) != NO_ERROR)
+        return 0;
+
+    sp<IMemoryHeap> heap;
+    uint32_t w, h;
+    PixelFormat f;
+    status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+    if (err != NO_ERROR)
+        return 0;
+
+    uint8_t* base = (uint8_t*)heap->getBase();
+    int fd = dup(STDOUT_FILENO);
+    write(fd, &w, 4);
+    write(fd, &h, 4);
+    write(fd, &f, 4);
+    write(fd, base, w*h*4);
+    close(fd);
+    return 0;
+}
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index a57a72f..d380a27 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -42,6 +42,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/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 8b54871..f55b746 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -46,6 +46,7 @@
 #include <media/mediametadataretriever.h>
 
 #include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
 
 #include <fcntl.h>
@@ -366,8 +367,13 @@
 
 static void writeSourcesToMP4(
         Vector<sp<MediaSource> > &sources, bool syncInfoPresent) {
+#if 0
     sp<MPEG4Writer> writer =
         new MPEG4Writer(gWriteMP4Filename.string());
+#else
+    sp<MPEG2TSWriter> writer =
+        new MPEG2TSWriter(gWriteMP4Filename.string());
+#endif
 
     // at most one minute.
     writer->setMaxFileDuration(60000000ll);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 73e8d31..b558318 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1297,7 +1297,16 @@
             reply.writeNoException();
             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();
@@ -2926,6 +2935,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 2ff88da..b8bbc88 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3377,12 +3377,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;
@@ -3427,6 +3435,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;
@@ -3435,7 +3459,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--;
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 61a8fc3..f0477e5 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -16,12 +16,16 @@
 
 package android.app;
 
+import com.android.internal.app.AlertController;
+
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Message;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.WindowManager;
@@ -30,8 +34,6 @@
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
-import com.android.internal.app.AlertController;
-
 /**
  * A subclass of Dialog that can display one, two or three buttons. If you only want to
  * display a String in this dialog box, use the setMessage() method.  If you
@@ -56,7 +58,10 @@
     private AlertController mAlert;
 
     protected AlertDialog(Context context) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+        this(context,
+                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                        ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                        : com.android.internal.R.style.Theme_Dialog_Alert);
     }
 
     protected AlertDialog(Context context, int theme) {
@@ -65,7 +70,10 @@
     }
 
     protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
-        super(context, com.android.internal.R.style.Theme_Dialog_Alert);
+        super(context,
+                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                : com.android.internal.R.style.Theme_Dialog_Alert);
         setCancelable(cancelable);
         setOnCancelListener(cancelListener);
         mAlert = new AlertController(context, this, getWindow());
@@ -266,12 +274,16 @@
     public static class Builder {
         private final AlertController.AlertParams P;
         private int mTheme;
+        private Context mWrappedContext;
         
         /**
          * Constructor using a context for this builder and the {@link AlertDialog} it creates.
          */
         public Builder(Context context) {
-            this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+            this(context,
+                    context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                    ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                    : com.android.internal.R.style.Theme_Dialog_Alert);
         }
 
         /**
@@ -284,6 +296,21 @@
         }
         
         /**
+         * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder.
+         * Applications should use this Context for obtaining LayoutInflaters for inflating views
+         * that will be used in the resulting dialogs, as it will cause views to be inflated with
+         * the correct theme.
+         *
+         * @return A Context for built Dialogs.
+         */
+        public Context getContext() {
+            if (mWrappedContext == null) {
+                mWrappedContext = new ContextThemeWrapper(P.mContext, mTheme);
+            }
+            return mWrappedContext;
+        }
+
+        /**
          * Set the title using the given resource id.
          *
          * @return This Builder object to allow for chaining of calls to set methods
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7497136..918ecf1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -69,13 +69,13 @@
 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;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.DropBoxManager;
 import android.os.Environment;
@@ -103,6 +103,8 @@
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
 import android.app.admin.DevicePolicyManager;
+import com.trustedlogic.trustednfc.android.NfcManager;
+import com.trustedlogic.trustednfc.android.INfcManager;
 
 import com.android.internal.os.IDropBoxManagerService;
 
@@ -173,6 +175,7 @@
     private static WifiManager sWifiManager;
     private static LocationManager sLocationManager;
     private static CountryDetector sCountryDetector;
+    private static NfcManager sNfcManager;
     private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
             new HashMap<String, SharedPreferencesImpl>();
 
@@ -968,6 +971,8 @@
             return getClipboardManager();
         } else if (WALLPAPER_SERVICE.equals(name)) {
             return getWallpaperManager();
+        } else if (NFC_SERVICE.equals(name)) {
+            return getNfcManager();
         } else if (DROPBOX_SERVICE.equals(name)) {
             return getDropBoxManager();
         } else if (DEVICE_POLICY_SERVICE.equals(name)) {
@@ -1063,8 +1068,13 @@
     private NotificationManager getNotificationManager() {
         synchronized (mSync) {
             if (mNotificationManager == null) {
+                final Context outerContext = getOuterContext();
                 mNotificationManager = new NotificationManager(
-                        new ContextThemeWrapper(getOuterContext(), com.android.internal.R.style.Theme_Dialog),
+                        new ContextThemeWrapper(outerContext,
+                                outerContext.getApplicationInfo().targetSdkVersion >=
+                                    Build.VERSION_CODES.HONEYCOMB
+                                ? com.android.internal.R.style.Theme_Holo_Dialog
+                                : com.android.internal.R.style.Theme_Dialog),
                         mMainThread.getHandler());
             }
         }
@@ -1214,6 +1224,21 @@
         return mDownloadManager;
     }
 
+    private NfcManager getNfcManager()
+    {
+        synchronized (sSync) {
+            if (sNfcManager == null) {
+                IBinder b = ServiceManager.getService(NFC_SERVICE);
+                if (b == null) {
+                    return null;
+                }
+                INfcManager service = INfcManager.Stub.asInterface(b);
+                sNfcManager = new NfcManager(service, mMainThread.getHandler());
+            }
+        }
+        return sNfcManager;
+    }
+
     @Override
     public int checkPermission(String permission, int pid, int uid) {
         if (permission == null) {
@@ -1632,22 +1657,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/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 8ba480d..37f8738 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.TextUtils.TruncateAt;
 import android.view.LayoutInflater;
@@ -82,7 +83,9 @@
             int year,
             int monthOfYear,
             int dayOfMonth) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert,
+        this(context, context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                        ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                        : com.android.internal.R.style.Theme_Dialog_Alert,
                 callBack, year, monthOfYear, dayOfMonth);
     }
 
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index a0be0cd..a178c04 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -25,6 +25,7 @@
 import android.content.DialogInterface;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -140,7 +141,10 @@
      */
     public Dialog(Context context, int theme) {
         mContext = new ContextThemeWrapper(
-            context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme);
+            context, theme == 0 ? 
+                    (context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                            ? com.android.internal.R.style.Theme_Holo_Dialog
+                                    : com.android.internal.R.style.Theme_Dialog) : theme);
         mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
         Window w = PolicyManager.makeNewWindow(mContext);
         mWindow = w;
@@ -173,7 +177,7 @@
     /**
      * Retrieve the Context this Dialog is running in.
      * 
-     * @return Context The Context that was supplied to the constructor.
+     * @return Context The Context used by the Dialog.
      */
     public final Context getContext() {
         return mContext;
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index e8dfac9..8e2389b 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -199,7 +199,7 @@
     public void setStyle(int style, int theme) {
         mStyle = style;
         if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
-            mTheme = android.R.style.Theme_Dialog_NoFrame;
+            mTheme = com.android.internal.R.style.Theme_Holo_Dialog_NoFrame;
         }
         if (theme != 0) {
             mTheme = theme;
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/app/DownloadManager.java
similarity index 99%
rename from core/java/android/net/DownloadManager.java
rename to core/java/android/app/DownloadManager.java
index 3279e8f..69c99cc 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -22,6 +22,8 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.database.CursorWrapper;
+import android.net.ConnectivityManager;
+import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.BaseColumns;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 901f117..4d73817 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -317,6 +317,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,
@@ -534,8 +536,9 @@
     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 DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
+    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;
+    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
 }
diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java
index bdea069..07a5a22 100644
--- a/core/java/android/app/ProgressDialog.java
+++ b/core/java/android/app/ProgressDialog.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -73,7 +74,10 @@
     private Handler mViewUpdateHandler;
     
     public ProgressDialog(Context context) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+        this(context,
+                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                : com.android.internal.R.style.Theme_Dialog_Alert);
     }
 
     public ProgressDialog(Context context, int theme) {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index de544fb..fa7f794 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -55,6 +55,18 @@
     public static final int DISABLE_NOTIFICATION_TICKER = 0x00000008;
 
     /**
+     * Flag for {@link #disable} to hide the center system info area.
+     */
+    public static final int DISABLE_SYSTEM_INFO = 0x00000010;
+
+    /**
+     * Flag for {@link #disable} to hide only the navigation buttons.  Don't use this
+     * unless you're the setup wizard.
+     */
+    public static final int DISABLE_NAVIGATION = 0x00000020;
+
+
+    /**
      * Re-enable all of the status bar features that you've disabled.
      */
     public static final int DISABLE_NONE = 0x00000000;
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 521d41c..381143c 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.format.DateFormat;
 import android.view.LayoutInflater;
@@ -76,7 +77,10 @@
     public TimePickerDialog(Context context,
             OnTimeSetListener callBack,
             int hourOfDay, int minute, boolean is24HourView) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert,
+        this(context,
+                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                        ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                        : com.android.internal.R.style.Theme_Dialog_Alert,
                 callBack, hourOfDay, minute, is24HourView);
     }
 
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 7e5f858..d308a5c 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -18,88 +18,104 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.server.BluetoothA2dpService;
 import android.content.Context;
-import android.os.ServiceManager;
-import android.os.RemoteException;
 import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.server.BluetoothA2dpService;
 import android.util.Log;
 
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.Set;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Set;
+
 
 /**
- * Public API for controlling the Bluetooth A2DP Profile Service.
+ * This class provides the public APIs to control the Bluetooth A2DP
+ * profile.
  *
- * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
- * Service via IPC.
+ *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothA2dp proxy object.
  *
- * Creating a BluetoothA2dp object will initiate a binding with the
- * BluetoothHeadset service. Users of this object should call close() when they
- * are finished, so that this proxy object can unbind from the service.
- *
- * Currently the BluetoothA2dp service runs in the system server and this
- * proxy object will be immediately bound to the service on construction.
- *
- * Currently this class provides methods to connect to A2DP audio sinks.
- *
- * @hide
+ * <p> Android only supports one connected Bluetooth A2dp device at a time.
+ * Each method is protected with its appropriate permission.
  */
-public final class BluetoothA2dp {
+public final class BluetoothA2dp implements BluetoothProfile {
     private static final String TAG = "BluetoothA2dp";
     private static final boolean DBG = false;
 
-    /** int extra for ACTION_SINK_STATE_CHANGED */
-    public static final String EXTRA_SINK_STATE =
-        "android.bluetooth.a2dp.extra.SINK_STATE";
-    /** int extra for ACTION_SINK_STATE_CHANGED */
-    public static final String EXTRA_PREVIOUS_SINK_STATE =
-        "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE";
-
-    /** Indicates the state of an A2DP audio sink has changed.
-     * This intent will always contain EXTRA_SINK_STATE,
-     * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE
-     * extras.
+    /**
+     * Intent used to broadcast the change in connection state of the A2DP
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SINK_STATE_CHANGED =
-        "android.bluetooth.a2dp.action.SINK_STATE_CHANGED";
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
 
-    public static final int STATE_DISCONNECTED = 0;
-    public static final int STATE_CONNECTING   = 1;
-    public static final int STATE_CONNECTED    = 2;
-    public static final int STATE_DISCONNECTING = 3;
-    /** Playing implies connected */
-    public static final int STATE_PLAYING    = 4;
+    /**
+     * Intent used to broadcast the change in the Playing state of the A2DP
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PLAYING_STATE_CHANGED =
+        "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
 
-    /** Default priority for a2dp devices that we try to auto-connect
-     * and allow incoming connections */
-    public static final int PRIORITY_AUTO_CONNECT = 1000;
-    /** Default priority for a2dp devices that should allow incoming
-     * connections */
-    public static final int PRIORITY_ON = 100;
-    /** Default priority for a2dp devices that should not allow incoming
-     * connections */
-    public static final int PRIORITY_OFF = 0;
-    /** Default priority when not set or when the device is unpaired */
-    public static final int PRIORITY_UNDEFINED = -1;
+    /**
+     * A2DP sink device is streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_PLAYING   =  10;
 
-    private final IBluetoothA2dp mService;
-    private final Context mContext;
+    /**
+     * A2DP sink device is NOT streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_NOT_PLAYING   =  11;
+
+    private ServiceListener mServiceListener;
+    private IBluetoothA2dp mService;
+    private BluetoothAdapter mAdapter;
 
     /**
      * Create a BluetoothA2dp proxy object for interacting with the local
      * Bluetooth A2DP service.
-     * @param c Context
+     *
      */
-    public BluetoothA2dp(Context c) {
-        mContext = c;
-
+    /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) {
         IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
+        mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
         if (b != null) {
             mService = IBluetoothA2dp.Stub.asInterface(b);
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this);
+            }
         } else {
             Log.w(TAG, "Bluetooth A2DP service not available!");
 
@@ -109,167 +125,222 @@
         }
     }
 
-    /** Initiate a connection to an A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when the
-     *  connection is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * {@inheritDoc}
+     * @hide
      */
-    public boolean connectSink(BluetoothDevice device) {
-        if (DBG) log("connectSink(" + device + ")");
-        try {
-            return mService.connectSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
-    /** Initiate disconnect from an A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when
-     *  disconnect is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * {@inheritDoc}
+     * @hide
      */
-    public boolean disconnectSink(BluetoothDevice device) {
-        if (DBG) log("disconnectSink(" + device + ")");
-        try {
-            return mService.disconnectSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
-    /** Initiate suspend from an A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when
-     *  suspend is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * {@inheritDoc}
+     */
+    public Set<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return toDeviceSet(mService.getConnectedDevices());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(new BluetoothDevice[0]);
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return toDeviceSet(new BluetoothDevice[0]);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return toDeviceSet(mService.getDevicesMatchingConnectionStates(states));
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(new BluetoothDevice[0]);
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return toDeviceSet(new BluetoothDevice[0]);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getState(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON) {
+              return false;
+            }
+            try {
+                return mService.setPriority(device, priority);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    public int getPriority(BluetoothDevice device) {
+        if (DBG) log("getPriority(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getPriority(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.PRIORITY_OFF;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.PRIORITY_OFF;
+    }
+
+    /**
+     * Check if A2DP profile is streaming music.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device BluetoothDevice device
+     */
+    public boolean isA2dpPlaying(BluetoothDevice device) {
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.isA2dpPlaying(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Initiate suspend from an A2DP sink.
+     *
+     * <p> This API will return false in scenarios like the A2DP
+     * device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
+     * intent will be broadcasted with the state. Users can get the
+     * state of the A2DP device from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote A2DP sink
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
      */
     public boolean suspendSink(BluetoothDevice device) {
-        try {
-            return mService.suspendSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.suspendSink(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
-    /** Initiate resume from an suspended A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when
-     *  resume is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * Initiate resume from a suspended A2DP sink.
+     *
+     * <p> This API will return false in scenarios like the A2DP
+     * device is not in suspended state etc. When this API returns,
+     * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
+     * intent will be broadcasted with the state. Users can get the
+     * state of the A2DP device from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote A2DP sink
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
      */
     public boolean resumeSink(BluetoothDevice device) {
-        try {
-            return mService.resumeSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.resumeSink(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
-    /** Check if a specified A2DP sink is connected.
-     *  @param device Remote BT device.
-     *  @return True if connected (or playing), false otherwise and on error.
-     *  @hide
-     */
-    public boolean isSinkConnected(BluetoothDevice device) {
-        if (DBG) log("isSinkConnected(" + device + ")");
-        int state = getSinkState(device);
-        return state == STATE_CONNECTED || state == STATE_PLAYING;
-    }
-
-    /** Check if any A2DP sink is connected.
-     * @return a unmodifiable set of connected A2DP sinks, or null on error.
-     * @hide
-     */
-    public Set<BluetoothDevice> getConnectedSinks() {
-        if (DBG) log("getConnectedSinks()");
-        try {
-            return Collections.unmodifiableSet(
-                    new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks())));
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return null;
-        }
-    }
-
-    /** Check if any A2DP sink is in Non Disconnected state
-     * i.e playing, connected, connecting, disconnecting.
-     * @return a unmodifiable set of connected A2DP sinks, or null on error.
-     * @hide
-     */
-    public Set<BluetoothDevice> getNonDisconnectedSinks() {
-        if (DBG) log("getNonDisconnectedSinks()");
-        try {
-            return Collections.unmodifiableSet(
-                    new HashSet<BluetoothDevice>(Arrays.asList(mService.getNonDisconnectedSinks())));
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return null;
-        }
-    }
-
-    /** Get the state of an A2DP sink
-     *  @param device Remote BT device.
-     *  @return State code, one of STATE_
-     *  @hide
-     */
-    public int getSinkState(BluetoothDevice device) {
-        if (DBG) log("getSinkState(" + device + ")");
-        try {
-            return mService.getSinkState(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return BluetoothA2dp.STATE_DISCONNECTED;
-        }
-    }
-
-    /**
-     * Set priority of a2dp sink.
-     * Priority is a non-negative integer. By default paired sinks will have
-     * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
-     * Sinks with priority greater than zero will accept incoming connections
-     * (if no sink is currently connected).
-     * Priority for unpaired sink must be PRIORITY_NONE.
-     * @param device Paired sink
-     * @param priority Integer priority, for example PRIORITY_AUTO or
-     *                 PRIORITY_NONE
-     * @return true if priority is set, false on error
-     */
-    public boolean setSinkPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setSinkPriority(" + device + ", " + priority + ")");
-        try {
-            return mService.setSinkPriority(device, priority);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Get priority of a2dp sink.
-     * @param device Sink
-     * @return non-negative priority, or negative error code on error.
-     */
-    public int getSinkPriority(BluetoothDevice device) {
-        if (DBG) log("getSinkPriority(" + device + ")");
-        try {
-            return mService.getSinkPriority(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return PRIORITY_OFF;
-        }
-    }
-
-    /** Helper for converting a state to a string.
+     /**
+     * Helper for converting a state to a string.
+     *
      * For debug use only - strings are not internationalized.
      * @hide
      */
@@ -285,12 +356,31 @@
             return "disconnecting";
         case STATE_PLAYING:
             return "playing";
+        case STATE_NOT_PLAYING:
+          return "not playing";
         default:
             return "<unknown state " + state + ">";
         }
     }
 
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
+    private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
+       return Collections.unmodifiableSet(
+          new HashSet<BluetoothDevice>(Arrays.asList(devices)));
+    }
+
     private static void log(String msg) {
-        Log.d(TAG, msg);
+      Log.d(TAG, msg);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 33fd395..21a4bd6 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -279,6 +280,61 @@
      */
     public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
 
+    /**
+     * Intent used to broadcast the change in connection state of the local
+     * Bluetooth adapter to a profile of the remote device. When the adapter is
+     * not connected to any profiles of any remote devices and it attempts a
+     * connection to a profile this intent will sent. Once connected, this intent
+     * will not be sent for any more connection attempts to any profiles of any
+     * remote device. When the adapter disconnects from the last profile its
+     * connected to of any remote device, this intent will be sent.
+     *
+     * <p> This intent is useful for applications that are only concerned about
+     * whether the local adapter is connected to any profile of any device and
+     * are not really concerned about which profile. For example, an application
+     * which displays an icon to display whether Bluetooth is connected or not
+     * can use this intent.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous.
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
+     *
+     * This extra represents the current connection state.
+     */
+    public static final String EXTRA_CONNECTION_STATE =
+        "android.bluetooth.adapter.extra.CONNECTION_STATE";
+
+    /**
+     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
+     *
+     * This extra represents the previous connection state.
+     */
+    public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
+          "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
+
+    /** The profile is in disconnected state */
+    public static final int STATE_DISCONNECTED  = 0;
+    /** The profile is in connecting state */
+    public static final int STATE_CONNECTING    = 1;
+    /** The profile is in connected state */
+    public static final int STATE_CONNECTED     = 2;
+    /** The profile is in disconnecting state */
+    public static final int STATE_DISCONNECTING = 3;
+
     /** @hide */
     public static final String BLUETOOTH_SERVICE = "bluetooth";
 
@@ -896,6 +952,54 @@
         return null;
     }
 
+    /*
+     * Get the profile proxy object associated with the profile.
+     *
+     * <p>Profile can be one of {@link BluetoothProfile.HEADSET} or
+     * {@link BluetoothProfile.A2DP}. Clients must implements
+     * {@link BluetoothProfile.ServiceListener} to get notified of
+     * the connection status and to get the proxy object.
+     *
+     * @param context Context of the application
+     * @param listener The service Listener for connection callbacks.
+     * @param profile
+     * @return true on success, false on error
+     */
+    public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
+                                   int profile) {
+        if (context == null || listener == null) return false;
+
+        if (profile == BluetoothProfile.HEADSET) {
+            BluetoothHeadset headset = new BluetoothHeadset(context, listener);
+            return true;
+        } else if (profile == BluetoothProfile.A2DP) {
+            BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Close the connection of the profile proxy to the Service.
+     *
+     * <p> Clients should call this when they are no longer using
+     * the proxy obtained from {@link #getProfileProxy}.
+     * Profile can be one of {@link BluetoothProfile#HEADSET} or
+     * {@link BluetoothProfile#A2DP}
+     *
+     * @param profile
+     * @param proxy Profile proxy object
+     */
+    public void closeProfileProxy(int profile, BluetoothProfile proxy) {
+        if (profile == BluetoothProfile.HEADSET) {
+            BluetoothHeadset headset = (BluetoothHeadset)proxy;
+            if (headset != null) {
+                headset.close();
+            }
+        }
+    }
+
     private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
         Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
         for (int i = 0; i < addresses.length; i++) {
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index 1fd7151..e460839 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Message;
+import android.bluetooth.BluetoothAdapter;
 import android.server.BluetoothA2dpService;
 import android.server.BluetoothService;
 import android.util.Log;
@@ -28,6 +29,8 @@
 import com.android.internal.util.HierarchicalState;
 import com.android.internal.util.HierarchicalStateMachine;
 
+import java.util.Set;
+
 /**
  * This class is the Profile connection state machine associated with a remote
  * device. When the device bonds an instance of this class is created.
@@ -91,12 +94,11 @@
     private BluetoothService mService;
     private BluetoothA2dpService mA2dpService;
     private BluetoothHeadset  mHeadsetService;
-    private boolean mHeadsetServiceConnected;
 
     private BluetoothDevice mDevice;
-    private int mHeadsetState;
-    private int mA2dpState;
-    private int mHidState;
+    private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mHidState = BluetoothProfile.STATE_DISCONNECTED;
 
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -105,32 +107,29 @@
             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
             if (!device.equals(mDevice)) return;
 
-            if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
-                int oldState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, 0);
-                int initiator = intent.getIntExtra(
-                    BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
-                    BluetoothHeadset.LOCAL_DISCONNECT);
-                mHeadsetState = newState;
-                if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
-                    initiator == BluetoothHeadset.REMOTE_DISCONNECT) {
-                    sendMessage(DISCONNECT_HFP_INCOMING);
-                }
-                if (newState == BluetoothHeadset.STATE_CONNECTED ||
-                    newState == BluetoothHeadset.STATE_DISCONNECTED) {
-                    sendMessage(TRANSITION_TO_STABLE);
-                }
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
-                int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
+            if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
                 mA2dpState = newState;
-                if ((oldState == BluetoothA2dp.STATE_CONNECTED ||
-                           oldState == BluetoothA2dp.STATE_PLAYING) &&
-                           newState == BluetoothA2dp.STATE_DISCONNECTED) {
+                if (oldState == BluetoothA2dp.STATE_CONNECTED &&
+                    newState == BluetoothA2dp.STATE_DISCONNECTED) {
                     sendMessage(DISCONNECT_A2DP_INCOMING);
                 }
-                if (newState == BluetoothA2dp.STATE_CONNECTED ||
-                    newState == BluetoothA2dp.STATE_DISCONNECTED) {
+                if (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED) {
+                    sendMessage(TRANSITION_TO_STABLE);
+                }
+            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+
+                mHeadsetState = newState;
+                if (oldState == BluetoothHeadset.STATE_CONNECTED &&
+                    newState == BluetoothHeadset.STATE_DISCONNECTED) {
+                    sendMessage(DISCONNECT_HFP_INCOMING);
+                }
+                if (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED) {
                     sendMessage(TRANSITION_TO_STABLE);
                 }
             } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
@@ -192,31 +191,31 @@
 
         IntentFilter filter = new IntentFilter();
         // Fine-grained state broadcasts
-        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
 
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
-        HeadsetServiceListener l = new HeadsetServiceListener();
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                BluetoothProfile.HEADSET);
     }
 
-    private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener {
-        public HeadsetServiceListener() {
-            mHeadsetService = new BluetoothHeadset(mContext, this);
-        }
-        public void onServiceConnected() {
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
             synchronized(BluetoothDeviceProfileState.this) {
-                mHeadsetServiceConnected = true;
+                mHeadsetService = (BluetoothHeadset) proxy;
             }
         }
-        public void onServiceDisconnected() {
+        public void onServiceDisconnected(int profile) {
             synchronized(BluetoothDeviceProfileState.this) {
-                mHeadsetServiceConnected = false;
+                mHeadsetService = null;
             }
         }
-    }
+    };
 
     private class BondedDevice extends HierarchicalState {
         @Override
@@ -276,19 +275,25 @@
                     if (isPhoneDocked(mDevice)) {
                         // Don't auto connect to docks.
                         break;
-                    } else if (!mHeadsetServiceConnected) {
+                    } else if (mHeadsetService == null) {
                         deferMessage(message);
                     } else {
                         if (mHeadsetService.getPriority(mDevice) ==
                               BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
-                              !mHeadsetService.isConnected(mDevice)) {
-                            mHeadsetService.connectHeadset(mDevice);
+                              mHeadsetService.getDevicesMatchingConnectionStates(
+                                  new int[] {BluetoothProfile.STATE_CONNECTED,
+                                             BluetoothProfile.STATE_CONNECTING,
+                                             BluetoothProfile.STATE_DISCONNECTING}).size() == 0) {
+                            mHeadsetService.connect(mDevice);
                         }
                         if (mA2dpService != null &&
-                              mA2dpService.getSinkPriority(mDevice) ==
+                              mA2dpService.getPriority(mDevice) ==
                               BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
-                              mA2dpService.getConnectedSinks().length == 0) {
-                            mA2dpService.connectSink(mDevice);
+                              mA2dpService.getDevicesMatchingConnectionStates(
+                                  new int[] {BluetoothA2dp.STATE_CONNECTED,
+                                             BluetoothProfile.STATE_CONNECTING,
+                                             BluetoothProfile.STATE_DISCONNECTING}).length == 0) {
+                            mA2dpService.connect(mDevice);
                         }
                         if (mService.getInputDevicePriority(mDevice) ==
                               BluetoothInputDevice.PRIORITY_AUTO_CONNECT) {
@@ -805,7 +810,7 @@
     synchronized void cancelCommand(int command) {
         if (command == CONNECT_HFP_OUTGOING ) {
             // Cancel the outgoing thread.
-            if (mHeadsetServiceConnected) {
+            if (mHeadsetService != null) {
                 mHeadsetService.cancelConnectThread();
             }
             // HeadsetService is down. Phone process most likely crashed.
@@ -823,12 +828,14 @@
         log("Processing command:" + command);
         switch(command) {
             case  CONNECT_HFP_OUTGOING:
-                if (mHeadsetService != null) {
+                if (mHeadsetService == null) {
+                    deferHeadsetMessage(command);
+                } else {
                     return mHeadsetService.connectHeadsetInternal(mDevice);
                 }
                 break;
             case CONNECT_HFP_INCOMING:
-                if (!mHeadsetServiceConnected) {
+                if (mHeadsetService == null) {
                     deferHeadsetMessage(command);
                 } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
                     return mHeadsetService.acceptIncomingConnect(mDevice);
@@ -849,7 +856,7 @@
             case CONNECT_HID_INCOMING:
                 return true;
             case DISCONNECT_HFP_OUTGOING:
-                if (!mHeadsetServiceConnected) {
+                if (mHeadsetService == null) {
                     deferHeadsetMessage(command);
                 } else {
                     if (mHeadsetService.getPriority(mDevice) ==
@@ -867,9 +874,9 @@
                 return true;
             case DISCONNECT_A2DP_OUTGOING:
                 if (mA2dpService != null) {
-                    if (mA2dpService.getSinkPriority(mDevice) ==
+                    if (mA2dpService.getPriority(mDevice) ==
                         BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
-                        mA2dpService.setSinkPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
+                        mA2dpService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
                     }
                     return mA2dpService.disconnectSinkInternal(mDevice);
                 }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index be21d46..0496b1f 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -18,6 +18,8 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -26,63 +28,66 @@
 import android.os.IBinder;
 import android.util.Log;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
  * Public API for controlling the Bluetooth Headset Service. This includes both
- * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will
- * attempt a handsfree connection first, and fall back to headset.
+ * Bluetooth Headset and Handsfree (v1.5) profiles.
  *
- * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
+ * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
  * Service via IPC.
  *
- * Creating a BluetoothHeadset object will create a binding with the
- * BluetoothHeadset service. Users of this object should call close() when they
- * are finished with the BluetoothHeadset, so that this proxy object can unbind
- * from the service.
+ * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHeadset proxy object. Use
+ * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
  *
- * This BluetoothHeadset object is not immediately bound to the
- * BluetoothHeadset service. Use the ServiceListener interface to obtain a
- * notification when it is bound, this is especially important if you wish to
- * immediately call methods on BluetoothHeadset after construction.
- *
- * Android only supports one connected Bluetooth Headset at a time.
- *
- * @hide
+ * <p> Android only supports one connected Bluetooth Headset at a time.
+ * Each method is protected with its appropriate permission.
  */
-public final class BluetoothHeadset {
-
+public final class BluetoothHeadset implements BluetoothProfile {
     private static final String TAG = "BluetoothHeadset";
     private static final boolean DBG = false;
 
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_STATE_CHANGED =
-            "android.bluetooth.headset.action.STATE_CHANGED";
     /**
-     * TODO(API release): Consider incorporating as new state in
-     * HEADSET_STATE_CHANGED
+     * Intent used to broadcast the change in connection state of the Headset
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in the Audio Connection state of the
+     * A2DP profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_AUDIO_STATE_CHANGED =
-            "android.bluetooth.headset.action.AUDIO_STATE_CHANGED";
-    public static final String EXTRA_STATE =
-            "android.bluetooth.headset.extra.STATE";
-    public static final String EXTRA_PREVIOUS_STATE =
-            "android.bluetooth.headset.extra.PREVIOUS_STATE";
-    public static final String EXTRA_AUDIO_STATE =
-            "android.bluetooth.headset.extra.AUDIO_STATE";
+        "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
 
-    /** Extra to be used with the Headset State change intent.
-     * This will be used only when Headset state changes to
-     * {@link #STATE_DISCONNECTED} from any previous state.
-     * This extra field is optional and will be used when
-     * we have deterministic information regarding whether
-     * the disconnect was initiated by the remote device or
-     * by the local adapter.
-     */
-    public static final String EXTRA_DISCONNECT_INITIATOR =
-            "android.bluetooth.headset.extra.DISCONNECT_INITIATOR";
 
     /**
      * Broadcast Action: Indicates a headset has posted a vendor-specific event.
@@ -124,100 +129,47 @@
     public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
             "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
 
+    /*
+     * Headset state when SCO audio is connected
+     * This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
+     */
+    public static final int STATE_AUDIO_CONNECTED = 10;
 
     /**
-     * TODO(API release): Consider incorporating as new state in
-     * HEADSET_STATE_CHANGED
+     * Headset state when SCO audio is NOT connected
+     * This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
      */
+    public static final int STATE_AUDIO_DISCONNECTED = 11;
+
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
     private IBluetoothHeadset mService;
-    private final Context mContext;
-    private final ServiceListener mServiceListener;
-
-    /** There was an error trying to obtain the state */
-    public static final int STATE_ERROR        = -1;
-    /** No headset currently connected */
-    public static final int STATE_DISCONNECTED = 0;
-    /** Connection attempt in progress */
-    public static final int STATE_CONNECTING   = 1;
-    /** A headset is currently connected */
-    public static final int STATE_CONNECTED    = 2;
-
-    /** A SCO audio channel is not established */
-    public static final int AUDIO_STATE_DISCONNECTED = 0;
-    /** A SCO audio channel is established */
-    public static final int AUDIO_STATE_CONNECTED = 1;
-
-    public static final int RESULT_FAILURE = 0;
-    public static final int RESULT_SUCCESS = 1;
-    /** Connection canceled before completion. */
-    public static final int RESULT_CANCELED = 2;
-
-    /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */
-    public static final int REMOTE_DISCONNECT = 0;
-    public static final int LOCAL_DISCONNECT = 1;
-
-
-    /** Default priority for headsets that  for which we will accept
-     * inconing connections and auto-connect */
-    public static final int PRIORITY_AUTO_CONNECT = 1000;
-    /** Default priority for headsets that  for which we will accept
-     * inconing connections but not auto-connect */
-    public static final int PRIORITY_ON = 100;
-    /** Default priority for headsets that should not be auto-connected
-     * and not allow incoming connections. */
-    public static final int PRIORITY_OFF = 0;
-    /** Default priority when not set or when the device is unpaired */
-    public static final int PRIORITY_UNDEFINED = -1;
-
-    /**
-     * An interface for notifying BluetoothHeadset IPC clients when they have
-     * been connected to the BluetoothHeadset service.
-     */
-    public interface ServiceListener {
-        /**
-         * Called to notify the client when this proxy object has been
-         * connected to the BluetoothHeadset service. Clients must wait for
-         * this callback before making IPC calls on the BluetoothHeadset
-         * service.
-         */
-        public void onServiceConnected();
-
-        /**
-         * Called to notify the client that this proxy object has been
-         * disconnected from the BluetoothHeadset service. Clients must not
-         * make IPC calls on the BluetoothHeadset service after this callback.
-         * This callback will currently only occur if the application hosting
-         * the BluetoothHeadset service, but may be called more often in future.
-         */
-        public void onServiceDisconnected();
-    }
+    BluetoothAdapter mAdapter;
 
     /**
      * Create a BluetoothHeadset proxy object.
      */
-    public BluetoothHeadset(Context context, ServiceListener l) {
+    /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
         mContext = context;
         mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
         if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
             Log.e(TAG, "Could not bind to Bluetooth Headset Service");
         }
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
     /**
      * Close the connection to the backing service.
      * Other public functions of BluetoothHeadset will return default error
      * results once close() has been called. Multiple invocations of close()
      * are ok.
      */
-    public synchronized void close() {
+    /*package*/ synchronized void close() {
         if (DBG) log("close()");
         if (mConnection != null) {
             mContext.unbindService(mConnection);
@@ -226,190 +178,212 @@
     }
 
     /**
-     * Get the current state of the Bluetooth Headset service.
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
-     *         object is currently not connected to the Headset service.
+     * {@inheritDoc}
+     * @hide
      */
-    public int getState(BluetoothDevice device) {
-        if (DBG) log("getState()");
-        if (mService != null) {
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.getState(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
-        return BluetoothHeadset.STATE_ERROR;
-    }
-
-    /**
-     * Get the BluetoothDevice for the current headset.
-     * @return current headset, or null if not in connected or connecting
-     *         state, or if this proxy object is not connected to the Headset
-     *         service.
-     */
-    public BluetoothDevice getCurrentHeadset() {
-        if (DBG) log("getCurrentHeadset()");
-        if (mService != null) {
-            try {
-                return mService.getCurrentHeadset();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return null;
-    }
-
-    /**
-     * Request to initiate a connection to a headset.
-     * This call does not block. Fails if a headset is already connecting
-     * or connected.
-     * Initiates auto-connection if device is null. Tries to connect to all
-     * devices with priority greater than PRIORITY_AUTO in descending order.
-     * @param device device to connect to, or null to auto-connect last connected
-     *               headset
-     * @return       false if there was a problem initiating the connection
-     *               procedure, and no further HEADSET_STATE_CHANGED intents
-     *               will be expected.
-     */
-    public boolean connectHeadset(BluetoothDevice device) {
-        if (DBG) log("connectHeadset(" + device + ")");
-        if (mService != null) {
-            try {
-                if (mService.connectHeadset(device)) {
-                    return true;
-                }
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Returns true if the specified headset is connected (does not include
-     * connecting). Returns false if not connected, or if this proxy object
-     * if not currently connected to the headset service.
+     * {@inheritDoc}
+     * @hide
      */
-    public boolean isConnected(BluetoothDevice device) {
-        if (DBG) log("isConnected(" + device + ")");
-        if (mService != null) {
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.isConnected(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+              Log.e(TAG, Log.getStackTraceString(new Throwable()));
+              return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Disconnects the current headset. Currently this call blocks, it may soon
-     * be made asynchornous. Returns false if this proxy object is
-     * not currently connected to the Headset service.
+     * {@inheritDoc}
      */
-    public boolean disconnectHeadset(BluetoothDevice device) {
-        if (DBG) log("disconnectHeadset()");
-        if (mService != null) {
+    public Set<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
             try {
-                mService.disconnectHeadset(device);
-                return true;
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(mService.getConnectedDevices());
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(new BluetoothDevice[0]);
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return toDeviceSet(new BluetoothDevice[0]);
     }
 
     /**
-     * Start BT Voice Recognition mode, and set up Bluetooth audio path.
-     * Returns false if there is no headset connected, or if the
-     * connected headset does not support voice recognition, or on
-     * error.
+     * {@inheritDoc}
      */
-    public boolean startVoiceRecognition() {
-        if (DBG) log("startVoiceRecognition()");
-        if (mService != null) {
+    public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
             try {
-                return mService.startVoiceRecognition();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(mService.getDevicesMatchingConnectionStates(states));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return toDeviceSet(new BluetoothDevice[0]);
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return toDeviceSet(new BluetoothDevice[0]);
     }
 
     /**
-     * Stop BT Voice Recognition mode, and shut down Bluetooth audio path.
-     * Returns false if there is no headset connected, or the connected
-     * headset is not in voice recognition mode, or on error.
+     * {@inheritDoc}
      */
-    public boolean stopVoiceRecognition() {
-        if (DBG) log("stopVoiceRecognition()");
-        if (mService != null) {
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getConnectionState(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.stopVoiceRecognition();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
     }
 
     /**
-     * Set priority of headset.
-     * Priority is a non-negative integer. By default paired headsets will have
-     * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
-     * Headsets with priority greater than zero will be auto-connected, and
-     * incoming connections will be accepted (if no other headset is
-     * connected).
-     * Auto-connection occurs at the following events: boot, incoming phone
-     * call, outgoing phone call.
-     * Headsets with priority equal to zero, or that are unpaired, are not
-     * auto-connected.
-     * Incoming connections are ignored regardless of priority if there is
-     * already a headset connected.
-     * @param device paired headset
-     * @param priority Integer priority, for example PRIORITY_AUTO or
-     *                 PRIORITY_NONE
-     * @return true if successful, false if there was some error
+     * {@inheritDoc}
+     * @hide
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON) {
+              return false;
+            }
             try {
                 return mService.setPriority(device, priority);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Get priority of headset.
-     * @param device headset
-     * @return non-negative priority, or negative error code on error
+     * {@inheritDoc}
+     * @hide
      */
     public int getPriority(BluetoothDevice device) {
         if (DBG) log("getPriority(" + device + ")");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
                 return mService.getPriority(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return PRIORITY_OFF;
+            }
         }
-        return -1;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return PRIORITY_OFF;
+    }
+
+    /**
+     * Start Bluetooth voice recognition. This methods sends the voice
+     * recognition AT command to the headset and establishes the
+     * audio connection.
+     *
+     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+     * {@link #EXTRA_STATE} will be set to {@link #STATE_AUDIO_CONNECTED}
+     * when the audio connection is established.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return false if there is no headset connected of if the
+     *               connected headset doesn't support voice recognition
+     *               or on error, true otherwise
+     */
+    public boolean startVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("startVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.startVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Stop Bluetooth Voice Recognition mode, and shut down the
+     * Bluetooth audio path.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return false if there is no headset connected
+     *               or on error, true otherwise
+     */
+    public boolean stopVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("stopVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.stopVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Check if Bluetooth SCO audio is connected.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return true if SCO is connected,
+     *         false otherwise or on error
+     */
+    public boolean isAudioConnected(BluetoothDevice device) {
+        if (DBG) log("isAudioConnected()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+              return mService.isAudioConnected(device);
+            } catch (RemoteException e) {
+              Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
     /**
@@ -420,24 +394,29 @@
      * boot. This is a good indicator for spammy headset/handsfree units that
      * can keep the device awake by polling for cellular status updates. As a
      * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
+     *
+     * @param device the bluetooth headset.
      * @return monotonically increasing battery usage hint, or a negative error
      *         code on error
      * @hide
      */
-    public int getBatteryUsageHint() {
+    public int getBatteryUsageHint(BluetoothDevice device) {
         if (DBG) log("getBatteryUsageHint()");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.getBatteryUsageHint();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getBatteryUsageHint(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return -1;
     }
+
     /**
      * Indicates if current platform supports voice dialing over bluetooth SCO.
+     *
      * @return true if voice dialing over bluetooth is supported, false otherwise.
      * @hide
      */
@@ -448,11 +427,13 @@
 
     /**
      * Cancel the outgoing connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean cancelConnectThread() {
         if (DBG) log("cancelConnectThread");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.cancelConnectThread();
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -465,11 +446,13 @@
 
     /**
      * Accept the incoming connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean acceptIncomingConnect(BluetoothDevice device) {
         if (DBG) log("acceptIncomingConnect");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.acceptIncomingConnect(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -481,12 +464,14 @@
     }
 
     /**
-     * Create the connect thread the incoming connection.
+     * Create the connect thread for the incoming connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean createIncomingConnect(BluetoothDevice device) {
         if (DBG) log("createIncomingConnect");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.createIncomingConnect(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -500,11 +485,12 @@
     /**
      * Connect to a Bluetooth Headset.
      * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean connectHeadsetInternal(BluetoothDevice device) {
         if (DBG) log("connectHeadsetInternal");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.connectHeadsetInternal(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -518,11 +504,12 @@
     /**
      * Disconnect a Bluetooth Headset.
      * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean disconnectHeadsetInternal(BluetoothDevice device) {
         if (DBG) log("disconnectHeadsetInternal");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                  return mService.disconnectHeadsetInternal(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -532,23 +519,61 @@
         }
         return false;
     }
+
+    /**
+     * Set the audio state of the Headset.
+     * Note: This is an internal function and shouldn't be exposed
+     *
+     * @hide
+     */
+    public boolean setAudioState(BluetoothDevice device, int state) {
+        if (DBG) log("setAudioState");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.setAudioState(device, state);
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+        }
+        return false;
+    }
+
     private ServiceConnection mConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
             mService = IBluetoothHeadset.Stub.asInterface(service);
+
             if (mServiceListener != null) {
-                mServiceListener.onServiceConnected();
+                mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this);
             }
         }
         public void onServiceDisconnected(ComponentName className) {
             if (DBG) Log.d(TAG, "Proxy object disconnected");
             mService = null;
             if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected();
+                mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
             }
         }
     };
 
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
+    private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
+       return Collections.unmodifiableSet(
+          new HashSet<BluetoothDevice>(Arrays.asList(devices)));
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
new file mode 100644
index 0000000..3b4c84c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+
+import java.util.Set;
+
+/**
+ * Public APIs for the Bluetooth Profiles.
+ *
+ * <p> Clients should call {@link BluetoothAdapter#getProfileProxy},
+ * to get the Profile Proxy. Each public profile implements this
+ * interface.
+ */
+public interface BluetoothProfile {
+
+    /**
+     * Extra for the connection state intents of the individual profiles.
+     *
+     * This extra represents the current connection state of the profile of the
+     * Bluetooth device.
+     */
+    public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+
+    /**
+     * Extra for the connection state intents of the individual profiles.
+     *
+     * This extra represents the previous connection state of the profile of the
+     * Bluetooth device.
+     */
+    public static final String EXTRA_PREVIOUS_STATE =
+        "android.bluetooth.profile.extra.PREVIOUS_STATE";
+
+    /** The profile is in disconnected state */
+    public static final int STATE_DISCONNECTED  = 0;
+    /** The profile is in connecting state */
+    public static final int STATE_CONNECTING    = 1;
+    /** The profile is in connected state */
+    public static final int STATE_CONNECTED     = 2;
+    /** The profile is in disconnecting state */
+    public static final int STATE_DISCONNECTING = 3;
+
+    /**
+     * Headset and Handsfree profile
+     */
+    public static final int HEADSET = 1;
+    /**
+     * A2DP profile.
+     */
+    public static final int A2DP = 2;
+
+    /**
+     * Default priority for devices that we try to auto-connect to and
+     * and allow incoming connections for the profile
+     * @hide
+     **/
+    public static final int PRIORITY_AUTO_CONNECT = 1000;
+
+    /**
+     *  Default priority for devices that allow incoming
+     * and outgoing connections for the profile
+     * @hide
+     **/
+    public static final int PRIORITY_ON = 100;
+
+    /**
+     * Default priority for devices that does not allow incoming
+     * connections and outgoing connections for the profile.
+     * @hide
+     **/
+    public static final int PRIORITY_OFF = 0;
+
+    /**
+     * Default priority when not set or when the device is unpaired
+     * @hide
+     * */
+    public static final int PRIORITY_UNDEFINED = -1;
+
+    /**
+     * Initiate connection to a profile of the remote bluetooth device.
+     *
+     * <p> Currently, the system supports only 1 connection to the
+     * A2DP and Headset/Handsfree profile. The API will automatically
+     * disconnect connected devices before connecting.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is already connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that
+     * connection state intent for the profile will be broadcasted with
+     * the state. Users can get the connection state of the profile
+     * from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
+     */
+    public boolean connect(BluetoothDevice device);
+
+    /**
+     * Initiate disconnection from a profile
+     *
+     * <p> This API will return false in scenarios like the profile on the
+     * Bluetooth device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that the connection state change
+     * intent will be broadcasted with the state. Users can get the
+     * disconnection state of the profile from this intent.
+     *
+     * <p> If the disconnection is initiated by a remote device, the state
+     * will transition from {@link #STATE_CONNECTED} to
+     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+     * host (local) device the state will transition from
+     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+     * state {@link #STATE_DISCONNECTED}. The transition to
+     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+     * two scenarios.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
+     */
+    public boolean disconnect(BluetoothDevice device);
+
+    /**
+     * Get connected devices for this specific profile.
+     *
+     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @return An unmodifiable set of devices. The set will be empty on error.
+     */
+    public Set<BluetoothDevice> getConnectedDevices();
+
+    /**
+     * Get a set of devices that match any of the given connection
+     * states.
+     *
+     * <p> If none of devices match any of the given states,
+     * an empty set will be returned.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param states Array of states. States can be one of
+     *              {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+     *              {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
+     * @return An unmodifiable set of devices. The set will be empty on error.
+     */
+    public Set<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states);
+
+    /**
+     * Get the current connection state of the profile
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Remote bluetooth device.
+     * @return State of the profile connection. One of
+     *               {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+     *               {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+     */
+    public int getConnectionState(BluetoothDevice device);
+
+    /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     *  Priority can be one of {@link #PRIORITY_ON} or
+     * {@link #PRIORITY_OFF},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     * @hide
+     */
+    public boolean setPriority(BluetoothDevice device, int priority);
+
+    /**
+     * Get the priority of the profile.
+     *
+     * <p> The priority can be any of:
+     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     * @hide
+     */
+    public int getPriority(BluetoothDevice device);
+
+    /**
+     * An interface for notifying BluetoothProfile IPC clients when they have
+     * been connected or disconnected to the service.
+     */
+    public interface ServiceListener {
+        /**
+         * Called to notify the client when the proxy object has been
+         * connected to the service.
+         * @param profile - One of {@link #HEADSET} or
+         *                  {@link #A2DP}
+         * @param proxy - One of {@link BluetoothHeadset} or
+         *                {@link BluetoothA2dp}
+         */
+        public void onServiceConnected(int profile, BluetoothProfile proxy);
+
+        /**
+         * Called to notify the client that this proxy object has been
+         * disconnected from the service.
+         * @param profile - One of {@link #HEADSET} or
+         *                  {@link #A2DP}
+         */
+        public void onServiceDisconnected(int profile);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
index ad70d0d..7f42baf 100644
--- a/core/java/android/bluetooth/BluetoothProfileState.java
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -59,16 +59,16 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
-            if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
-                if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
-                    newState == BluetoothHeadset.STATE_DISCONNECTED)) {
+            if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                if (mProfile == HFP && (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED)) {
                     sendMessage(TRANSITION_TO_STABLE);
                 }
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
-                if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED ||
-                    newState == BluetoothA2dp.STATE_DISCONNECTED)) {
+            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                if (mProfile == A2DP && (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED)) {
                     sendMessage(TRANSITION_TO_STABLE);
                 }
             } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
@@ -89,8 +89,8 @@
         setInitialState(mStableState);
 
         IntentFilter filter = new IntentFilter();
-        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
         context.registerReceiver(mBroadcastReceiver, filter);
     }
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 40f1058..c5044c2 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -19,21 +19,25 @@
 import android.bluetooth.BluetoothDevice;
 
 /**
- * System private API for Bluetooth A2DP service
+ * APIs for Bluetooth A2DP service
  *
- * {@hide}
+ * @hide
  */
 interface IBluetoothA2dp {
-    boolean connectSink(in BluetoothDevice device);
-    boolean disconnectSink(in BluetoothDevice device);
+    // Public API
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    // change to Set<> once AIDL supports
+    BluetoothDevice[] getConnectedDevices();
+    BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    boolean setPriority(in BluetoothDevice device, int priority);
+    int getPriority(in BluetoothDevice device);
+    boolean isA2dpPlaying(in BluetoothDevice device);
+
+    // Internal APIs
     boolean suspendSink(in BluetoothDevice device);
     boolean resumeSink(in BluetoothDevice device);
-    BluetoothDevice[] getConnectedSinks();  // change to Set<> once AIDL supports
-    BluetoothDevice[] getNonDisconnectedSinks();  // change to Set<> once AIDL supports
-    int getSinkState(in BluetoothDevice device);
-    boolean setSinkPriority(in BluetoothDevice device, int priority);
-    int getSinkPriority(in BluetoothDevice device);
-
     boolean connectSinkInternal(in BluetoothDevice device);
     boolean disconnectSinkInternal(in BluetoothDevice device);
 }
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index d96f0ca..8bcf103 100644
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -19,25 +19,32 @@
 import android.bluetooth.BluetoothDevice;
 
 /**
- * System private API for Bluetooth Headset service
+ * API for Bluetooth Headset service
  *
  * {@hide}
  */
 interface IBluetoothHeadset {
-    int getState(in BluetoothDevice device);
-    BluetoothDevice getCurrentHeadset();
-    boolean connectHeadset(in BluetoothDevice device);
-    void disconnectHeadset(in BluetoothDevice device);
-    boolean isConnected(in BluetoothDevice device);
-    boolean startVoiceRecognition();
-    boolean stopVoiceRecognition();
+    // Public API
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    // Change to Set<> when AIDL supports
+    BluetoothDevice[] getConnectedDevices();
+    BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
     boolean setPriority(in BluetoothDevice device, int priority);
     int getPriority(in BluetoothDevice device);
-    int getBatteryUsageHint();
+    boolean startVoiceRecognition(in BluetoothDevice device);
+    boolean stopVoiceRecognition(in BluetoothDevice device);
+    boolean isAudioConnected(in BluetoothDevice device);
 
+    // APIs that can be made public in future
+    int getBatteryUsageHint(in BluetoothDevice device);
+
+    // Internal functions, not be made public
     boolean createIncomingConnect(in BluetoothDevice device);
     boolean acceptIncomingConnect(in BluetoothDevice device);
     boolean cancelConnectThread();
     boolean connectHeadsetInternal(in BluetoothDevice device);
     boolean disconnectHeadsetInternal(in BluetoothDevice device);
+    boolean setAudioState(in BluetoothDevice device, int state);
 }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index d3c1b4e..6bb32c1 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -561,6 +561,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 22feb9a..3289120 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);
 
@@ -187,18 +194,28 @@
      * @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) {
+        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);
         }
     }
 
@@ -224,15 +241,14 @@
         if (provider == null) {
             return null;
         }
+
         try {
             return provider.getStreamTypes(url, mimeTypeFilter);
         } catch (RemoteException e) {
             return null;
-        } catch (java.lang.Exception e) {
-            return null;
         } finally {
-            releaseProvider(provider);
-        }
+			releaseProvider(provider);
+		}
     }
 
     /**
@@ -821,14 +837,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;
         }
@@ -840,6 +855,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 3d29379..9c8d698 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1235,7 +1235,7 @@
      * <dt> {@link #UI_MODE_SERVICE} ("uimode")
      * <dd> An {@link android.app.UiModeManager} for controlling UI modes.
      * <dt> {@link #DOWNLOAD_SERVICE} ("download")
-     * <dd> A {@link android.net.DownloadManager} for requesting HTTP downloads
+     * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
      * </dl>
      * 
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -1284,7 +1284,7 @@
      * @see #UI_MODE_SERVICE
      * @see android.app.UiModeManager
      * @see #DOWNLOAD_SERVICE
-     * @see android.net.DownloadManager
+     * @see android.app.DownloadManager
      */
     public abstract Object getSystemService(String name);
 
@@ -1576,7 +1576,7 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
-     * {@link android.net.DownloadManager} for requesting HTTP downloads.
+     * {@link android.app.DownloadManager} for requesting HTTP downloads.
      *
      * @see #getSystemService
      */
@@ -1592,6 +1592,16 @@
     public static final String SIP_SERVICE = "sip";
 
     /**
+     * Use with {@link #getSystemService} to retrieve an
+     * {@link com.trustedlogic.trustednfc.android.INfcManager.INfcManager} for
+     * accessing NFC methods.
+     *
+     * @see #getSystemService
+     * @hide
+     */
+    public static final String NFC_SERVICE = "nfc";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1a3bcc4..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.
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 92a8a99..8ef639b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -36,6 +36,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -332,7 +333,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
             }
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index 838c5ff..7b962e5 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -20,9 +20,9 @@
 import android.os.Parcelable;
 
 /**
- * Basic information about a Opaque Binary Blob (OBB) that reflects
- * the info from the footer on the OBB file.
- * @hide
+ * Basic information about a Opaque Binary Blob (OBB) that reflects the info
+ * from the footer on the OBB file. This information may be manipulated by a
+ * developer with the <code>obbtool</code> program in the Android SDK.
  */
 public class ObbInfo implements Parcelable {
     /** Flag noting that this OBB is an overlay patch for a base OBB. */
@@ -43,7 +43,8 @@
      */
     public int flags;
 
-    public ObbInfo() {
+    // Only allow things in this package to instantiate.
+    /* package */ ObbInfo() {
     }
 
     public String toString() {
diff --git a/core/java/android/content/res/ObbScanner.java b/core/java/android/content/res/ObbScanner.java
index eb383c3..a3f141e 100644
--- a/core/java/android/content/res/ObbScanner.java
+++ b/core/java/android/content/res/ObbScanner.java
@@ -16,25 +16,43 @@
 
 package android.content.res;
 
+import java.io.File;
+import java.io.IOException;
+
 /**
- * Class to scan Opaque Binary Blob (OBB) files.
- * @hide
+ * Class to scan Opaque Binary Blob (OBB) files. Use this to get information
+ * about an OBB file for use in a program via {@link ObbInfo}.
  */
 public class ObbScanner {
     // Don't allow others to instantiate this class
     private ObbScanner() {}
 
-    public static ObbInfo getObbInfo(String filePath) {
+    /**
+     * Scan a file for OBB information.
+     * 
+     * @param filePath path to the OBB file to be scanned.
+     * @return ObbInfo object information corresponding to the file path
+     * @throws IllegalArgumentException if the OBB file couldn't be found
+     * @throws IOException if the OBB file couldn't be read
+     */
+    public static ObbInfo getObbInfo(String filePath) throws IOException {
         if (filePath == null) {
-            return null;
+            throw new IllegalArgumentException("file path cannot be null");
         }
 
-        ObbInfo obbInfo = new ObbInfo();
-        if (!getObbInfo_native(filePath, obbInfo)) {
-            throw new IllegalArgumentException("Could not read OBB file: " + filePath);
+        final File obbFile = new File(filePath);
+        if (!obbFile.exists()) {
+            throw new IllegalArgumentException("OBB file does nto exist: " + filePath);
         }
+
+        final String canonicalFilePath = obbFile.getCanonicalPath();
+
+        ObbInfo obbInfo = new ObbInfo();
+        getObbInfo_native(canonicalFilePath, obbInfo);
+
         return obbInfo;
     }
 
-    private native static boolean getObbInfo_native(String filePath, ObbInfo obbInfo);
+    private native static void getObbInfo_native(String filePath, ObbInfo obbInfo)
+            throws IOException;
 }
diff --git a/core/java/android/database/sqlite/DatabaseConnectionPool.java b/core/java/android/database/sqlite/DatabaseConnectionPool.java
index 4f5c4e6..39a9d23 100644
--- a/core/java/android/database/sqlite/DatabaseConnectionPool.java
+++ b/core/java/android/database/sqlite/DatabaseConnectionPool.java
@@ -58,16 +58,14 @@
     /**
      * close all database connections in the pool - even if they are in use!
      */
-    /* package */ void close() {
-        synchronized(mParentDbObj) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Closing the connection pool on " + mParentDbObj.getPath() + toString());
-            }
-            for (int i = mPool.size() - 1; i >= 0; i--) {
-                mPool.get(i).mDb.close();
-            }
-            mPool.clear();
+    /* package */ synchronized void close() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Closing the connection pool on " + mParentDbObj.getPath() + toString());
         }
+        for (int i = mPool.size() - 1; i >= 0; i--) {
+            mPool.get(i).mDb.close();
+        }
+        mPool.clear();
     }
 
     /**
@@ -77,76 +75,73 @@
      * the compiled statement for this sql.
      * @return the Database connection that the caller can use
      */
-    /* package */ SQLiteDatabase get(String sql) {
+    /* package */ synchronized SQLiteDatabase get(String sql) {
         SQLiteDatabase db = null;
         PoolObj poolObj = null;
-        synchronized(mParentDbObj) {
-            int poolSize = mPool.size();
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                assert sql != null;
-                doAsserts();
-            }
-            if (getFreePoolSize() == 0) {
-                // no free ( = available) connections
-                if (mMaxPoolSize == poolSize) {
-                    // maxed out. can't open any more connections.
-                    // let the caller wait on one of the pooled connections
-                    // preferably a connection caching the pre-compiled statement of the given SQL
-                    if (mMaxPoolSize == 1) {
-                        poolObj = mPool.get(0);
-                    } else {
-                        for (int i = 0; i < mMaxPoolSize; i++) {
-                            if (mPool.get(i).mDb.isSqlInStatementCache(sql)) {
-                                poolObj = mPool.get(i);
-                                break;
-                            }
-                        }
-                        if (poolObj == null) {
-                            // there are no database connections with the given SQL pre-compiled.
-                            // ok to return any of the connections.
-                            if (rand == null) {
-                                rand = new Random(SystemClock.elapsedRealtime());
-                            }
-                            poolObj = mPool.get(rand.nextInt(mMaxPoolSize));
-                        }
-                    }
-                    db = poolObj.mDb;
-                } else {
-                    // create a new connection and add it to the pool, since we haven't reached
-                    // max pool size allowed
-                    db = mParentDbObj.createPoolConnection((short)(poolSize + 1));
-                    poolObj = new PoolObj(db);
-                    mPool.add(poolSize, poolObj);
-                }
-            } else {
-                // there are free connections available. pick one
+        int poolSize = mPool.size();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            assert sql != null;
+            doAsserts();
+        }
+        if (getFreePoolSize() == 0) {
+            // no free ( = available) connections
+            if (mMaxPoolSize == poolSize) {
+                // maxed out. can't open any more connections.
+                // let the caller wait on one of the pooled connections
                 // preferably a connection caching the pre-compiled statement of the given SQL
-                for (int i = 0; i < poolSize; i++) {
-                    if (mPool.get(i).isFree() && mPool.get(i).mDb.isSqlInStatementCache(sql)) {
-                        poolObj = mPool.get(i);
-                        break;
-                    }
-                }
-                if (poolObj == null) {
-                    // didn't find a free database connection with the given SQL already
-                    // pre-compiled. return a free connection (this means, the same SQL could be
-                    // pre-compiled on more than one database connection. potential wasted memory.)
-                    for (int i = 0; i < poolSize; i++) {
-                        if (mPool.get(i).isFree()) {
+                if (mMaxPoolSize == 1) {
+                    poolObj = mPool.get(0);
+                } else {
+                    for (int i = 0; i < mMaxPoolSize; i++) {
+                        if (mPool.get(i).mDb.isInStatementCache(sql)) {
                             poolObj = mPool.get(i);
                             break;
                         }
                     }
+                    if (poolObj == null) {
+                        // there are no database connections with the given SQL pre-compiled.
+                        // ok to return any of the connections.
+                        if (rand == null) {
+                            rand = new Random(SystemClock.elapsedRealtime());
+                        }
+                        poolObj = mPool.get(rand.nextInt(mMaxPoolSize));
+                    }
                 }
                 db = poolObj.mDb;
+            } else {
+                // create a new connection and add it to the pool, since we haven't reached
+                // max pool size allowed
+                db = mParentDbObj.createPoolConnection((short)(poolSize + 1));
+                poolObj = new PoolObj(db);
+                mPool.add(poolSize, poolObj);
             }
-
-            assert poolObj != null;
-            assert poolObj.mDb == db;
-
-            poolObj.acquire();
+        } else {
+            // there are free connections available. pick one
+            // preferably a connection caching the pre-compiled statement of the given SQL
+            for (int i = 0; i < poolSize; i++) {
+                if (mPool.get(i).isFree() && mPool.get(i).mDb.isInStatementCache(sql)) {
+                    poolObj = mPool.get(i);
+                    break;
+                }
+            }
+            if (poolObj == null) {
+                // didn't find a free database connection with the given SQL already
+                // pre-compiled. return a free connection (this means, the same SQL could be
+                // pre-compiled on more than one database connection. potential wasted memory.)
+                for (int i = 0; i < poolSize; i++) {
+                    if (mPool.get(i).isFree()) {
+                        poolObj = mPool.get(i);
+                        break;
+                    }
+                }
+            }
+            db = poolObj.mDb;
         }
 
+        assert poolObj != null;
+        assert poolObj.mDb == db;
+
+        poolObj.acquire();
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "END get-connection: " + toString() + poolObj.toString());
         }
@@ -159,29 +154,25 @@
      * release the given database connection back to the pool.
      * @param db the connection to be released
      */
-    /* package */ void release(SQLiteDatabase db) {
-        PoolObj poolObj;
-        synchronized(mParentDbObj) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                assert db.mConnectionNum > 0;
-                doAsserts();
-                assert mPool.get(db.mConnectionNum - 1).mDb == db;
-            }
-
-            poolObj = mPool.get(db.mConnectionNum - 1);
-
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "BEGIN release-conn: " + toString() + poolObj.toString());
-            }
-
-            if (poolObj.isFree()) {
-                throw new IllegalStateException("Releasing object already freed: " +
-                        db.mConnectionNum);
-            }
-
-            poolObj.release();
+    /* package */ synchronized void release(SQLiteDatabase db) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            assert db.mConnectionNum > 0;
+            doAsserts();
+            assert mPool.get(db.mConnectionNum - 1).mDb == db;
         }
 
+        PoolObj poolObj = mPool.get(db.mConnectionNum - 1);
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "BEGIN release-conn: " + toString() + poolObj.toString());
+        }
+
+        if (poolObj.isFree()) {
+            throw new IllegalStateException("Releasing object already freed: " +
+                    db.mConnectionNum);
+        }
+
+        poolObj.release();
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "END release-conn: " + toString() + poolObj.toString());
         }
@@ -191,12 +182,10 @@
      * Returns a list of all database connections in the pool (both free and busy connections).
      * This method is used when "adb bugreport" is done.
      */
-    /* package */ ArrayList<SQLiteDatabase> getConnectionList() {
+    /* package */ synchronized ArrayList<SQLiteDatabase> getConnectionList() {
         ArrayList<SQLiteDatabase> list = new ArrayList<SQLiteDatabase>();
-        synchronized(mParentDbObj) {
-            for (int i = mPool.size() - 1; i >= 0; i--) {
-                list.add(mPool.get(i).mDb);
-            }
+        for (int i = mPool.size() - 1; i >= 0; i--) {
+            list.add(mPool.get(i).mDb);
         }
         return list;
     }
@@ -246,16 +235,14 @@
         }
     }
 
-    /* package */ void setMaxPoolSize(int size) {
-        synchronized(mParentDbObj) {
-            mMaxPoolSize = size;
-        }
+    /** only used for testing purposes. */
+    /* package */ synchronized void setMaxPoolSize(int size) {
+        mMaxPoolSize = size;
     }
 
-    /* package */ int getMaxPoolSize() {
-        synchronized(mParentDbObj) {
-            return mMaxPoolSize;
-        }
+    /** only used for testing purposes. */
+    /* package */ synchronized int getMaxPoolSize() {
+        return mMaxPoolSize;
     }
 
     /** only used for testing purposes. */
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 588384b..6ed1a90 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -88,13 +88,9 @@
         mInUse = false;
     }
 
-    /* package */ synchronized boolean isInUse() {
-        return mInUse;
-    }
-
     /* package */ synchronized void releaseIfNotInUse() {
         // if it is not in use, release its memory from the database
-        if (!isInUse()) {
+        if (!mInUse) {
             releaseSqlStatement();
         }
     }
@@ -110,7 +106,7 @@
             // but if the database itself is not closed and is GC'ed, then
             // all sub-objects attached to the database could end up getting GC'ed too.
             // in that case, don't print any warning.
-            if (!mInUse) {
+            if (mInUse) {
                 int len = mSqlStmt.length();
                 Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
                         "that you explicitly call close() on your cursor: " +
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 4cb7026..f0d0fb4 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -276,6 +276,7 @@
      */
     // default statement-cache size per database connection ( = instance of this class)
     private int mMaxSqlCacheSize = 25;
+    // guarded by itself
     /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
         new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
             @Override
@@ -306,8 +307,9 @@
     public static final int MAX_SQL_CACHE_SIZE = 100;
     private boolean mCacheFullWarning;
 
-    /** maintain stats about number of cache hits and misses */
+    /** Number of cache hits on this database connection. guarded by {@link #mCompiledQueries}. */
     private int mNumCacheHits;
+    /** Number of cache misses on this database connection. guarded by {@link #mCompiledQueries}. */
     private int mNumCacheMisses;
 
     /** Used to find out where this object was created in case it never got closed. */
@@ -344,6 +346,9 @@
 
     private static final String MEMORY_DB_PATH = ":memory:";
 
+    /** set to true if the database has attached databases */
+    private volatile boolean mHasAttachedDbs = false;
+
     /** stores reference to all databases opened in the current process. */
     private static ArrayList<WeakReference<SQLiteDatabase>> mActiveDatabases =
             new ArrayList<WeakReference<SQLiteDatabase>>();
@@ -1049,13 +1054,18 @@
      */
     public void close() {
         if (!isOpen()) {
-            return; // already closed
+            return;
         }
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum);
         }
         lock();
         try {
+            // some other thread could have closed this database while I was waiting for lock.
+            // check the database state
+            if (!isOpen()) {
+                return;
+            }
             closeClosable();
             // finalize ALL statements queued up so far
             closePendingStatements();
@@ -1831,6 +1841,9 @@
         logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
         executeSql(sql, null);
 
+        if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
+            mHasAttachedDbs = true;
+        }
         // Log commit statements along with the most recently executed
         // SQL statement for disambiguation.
         if (stmtType == DatabaseUtils.STATEMENT_COMMIT) {
@@ -2102,7 +2115,7 @@
                 return;
             }
 
-            if (!isCacheFullWarningLogged() && mCompiledQueries.size() == mMaxSqlCacheSize) {
+            if (!mCacheFullWarning && mCompiledQueries.size() == mMaxSqlCacheSize) {
                 /*
                  * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
                  * log a warning.
@@ -2110,7 +2123,7 @@
                  */
                 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
                         getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
-                setCacheFullWarningLogged();
+                mCacheFullWarning = true;
             } 
             /* add the given SQLiteCompiledSql compiledStatement to cache.
              * no need to worry about the cache size - because {@link #mCompiledQueries}
@@ -2134,14 +2147,16 @@
      * From the compiledQueries cache, returns the compiled-statement-id for the given SQL.
      * Returns null, if not found in the cache.
      */
-    /* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) {
-        SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
-        if (compiledStatement == null) {
-            mNumCacheMisses++;
-            return null;
+    /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
+        synchronized (mCompiledQueries) {
+            SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
+            if (compiledStatement == null) {
+                mNumCacheMisses++;
+                return null;
+            }
+            mNumCacheHits++;
+            return compiledStatement;
         }
-        mNumCacheHits++;
-        return compiledStatement;
     }
 
     /**
@@ -2159,7 +2174,7 @@
      * the value set with previous setMaxSqlCacheSize() call.
      */
     public void setMaxSqlCacheSize(int cacheSize) {
-        synchronized(this) {
+        synchronized(mCompiledQueries) {
             if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
                 throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
             } else if (cacheSize < mMaxSqlCacheSize) {
@@ -2170,30 +2185,40 @@
         }
     }
 
-    /* package */ boolean isSqlInStatementCache(String sql) {
+    /* package */ boolean isInStatementCache(String sql) {
         synchronized (mCompiledQueries) {
             return mCompiledQueries.containsKey(sql);
         }
     }
 
-    private synchronized boolean isCacheFullWarningLogged() {
-        return mCacheFullWarning;
+    /* package */ void releaseCompiledSqlObj(SQLiteCompiledSql compiledSql) {
+        synchronized (mCompiledQueries) {
+            if (mCompiledQueries.containsValue(compiledSql)) {
+                // it is in cache - reset its inUse flag
+                compiledSql.release();
+            } else {
+                // it is NOT in cache. finalize it.
+                compiledSql.releaseSqlStatement();
+            }
+        }
     }
 
-    private synchronized void setCacheFullWarningLogged() {
-        mCacheFullWarning = true;
+    private int getCacheHitNum() {
+        synchronized(mCompiledQueries) {
+            return mNumCacheHits;
+        }
     }
 
-    private synchronized int getCacheHitNum() {
-        return mNumCacheHits;
+    private int getCacheMissNum() {
+        synchronized(mCompiledQueries) {
+            return mNumCacheMisses;
+        }
     }
 
-    private synchronized int getCacheMissNum() {
-        return mNumCacheMisses;
-    }
-
-    private synchronized int getCachesize() {
-        return mCompiledQueries.size();
+    private int getCachesize() {
+        synchronized(mCompiledQueries) {
+            return mCompiledQueries.size();
+        }
     }
 
     /* package */ void finalizeStatementLater(int id) {
@@ -2279,26 +2304,34 @@
      *
      * @return true if write-ahead-logging is set. false otherwise
      */
-    public synchronized boolean enableWriteAheadLogging() {
-        if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
-            Log.i(TAG, "can't enable WAL for memory databases.");
-            return false;
-        }
-
-        // make sure this database has NO attached databases because sqlite's write-ahead-logging
-        // doesn't work for databases with attached databases
-        if (getAttachedDbs().size() > 1) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG,
-                        "this database: " + mPath + " has attached databases. can't  enable WAL.");
+    public boolean enableWriteAheadLogging() {
+        // acquire lock - no that no other thread is enabling WAL at the same time
+        lock();
+        try {
+            if (mConnectionPool != null) {
+                // already enabled
+                return true;
             }
-            return false;
-        }
-        if (mConnectionPool == null) {
+            if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
+                Log.i(TAG, "can't enable WAL for memory databases.");
+                return false;
+            }
+
+            // make sure this database has NO attached databases because sqlite's write-ahead-logging
+            // doesn't work for databases with attached databases
+            if (mHasAttachedDbs) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG,
+                            "this database: " + mPath + " has attached databases. can't  enable WAL.");
+                }
+                return false;
+            }
             mConnectionPool = new DatabaseConnectionPool(this);
             setJournalMode(mPath, "WAL");
+            return true;
+        } finally {
+            unlock();
         }
-        return true;
     }
 
     /**
@@ -2306,13 +2339,18 @@
      * @hide
      */
     public void disableWriteAheadLogging() {
-        synchronized (this) {
+        // grab database lock so that writeAheadLogging is not disabled from 2 different threads
+        // at the same time
+        lock();
+        try {
             if (mConnectionPool == null) {
-                return;
+                return; // already disabled
             }
             mConnectionPool.close();
-            mConnectionPool = null;
             setJournalMode(mPath, "TRUNCATE");
+            mConnectionPool = null;
+        } finally {
+            unlock();
         }
     }
 
@@ -2338,32 +2376,6 @@
         }
     }
 
-    /**
-     * Sets the database connection handle pool size to the given value.
-     * Database connection handle pool is enabled when the app calls
-     * {@link #enableWriteAheadLogging()}.
-     * <p>
-     * The default connection handle pool is set by the system by taking into account various
-     * aspects of the device, such as memory, number of cores etc. It is recommended that
-     * applications use the default pool size set by the system.
-     *
-     * @param size the value the connection handle pool size should be set to.
-     */
-    public void setConnectionPoolSize(int size) {
-        synchronized(this) {
-            if (mConnectionPool == null) {
-                throw new IllegalStateException("connection pool not enabled");
-            }
-            int i = mConnectionPool.getMaxPoolSize();
-            if (size < i) {
-                throw new IllegalArgumentException(
-                        "cannot set max pool size to a value less than the current max value(=" +
-                        i + ")");
-            }
-            mConnectionPool.setMaxPoolSize(size);
-        }
-    }
-
     /* package */ SQLiteDatabase createPoolConnection(short connectionNum) {
         SQLiteDatabase db = openDatabase(mPath, mFactory, mFlags, mErrorHandler, connectionNum);
         db.mParentConnObj = this;
@@ -2428,73 +2440,74 @@
      */
     /* package */ static ArrayList<DbStats> getDbStats() {
         ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
-//        // make a local copy of mActiveDatabases - so that this method is not competing
-//        // for synchronization lock on mActiveDatabases
-//        ArrayList<WeakReference<SQLiteDatabase>> tempList;
-//        synchronized(mActiveDatabases) {
-//            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
-//        }
-//        for (WeakReference<SQLiteDatabase> w : tempList) {
-//            SQLiteDatabase db = w.get();
-//            if (db == null || !db.isOpen()) {
-//                continue;
-//            }
-//
-//            synchronized (db) {
-//                try {
-//                    // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
-//                    int lookasideUsed = db.native_getDbLookaside();
-//
-//                    // get the lastnode of the dbname
-//                    String path = db.getPath();
-//                    int indx = path.lastIndexOf("/");
-//                    String lastnode = path.substring((indx != -1) ? ++indx : 0);
-//
-//                    // get list of attached dbs and for each db, get its size and pagesize
-//                    ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
-//                    if (attachedDbs == null) {
-//                        continue;
-//                    }
-//                    for (int i = 0; i < attachedDbs.size(); i++) {
-//                        Pair<String, String> p = attachedDbs.get(i);
-//                        long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
-//                                + ".page_count;", null);
-//
-//                        // first entry in the attached db list is always the main database
-//                        // don't worry about prefixing the dbname with "main"
-//                        String dbName;
-//                        if (i == 0) {
-//                            dbName = lastnode;
-//                        } else {
-//                            // lookaside is only relevant for the main db
-//                            lookasideUsed = 0;
-//                            dbName = "  (attached) " + p.first;
-//                            // if the attached db has a path, attach the lastnode from the path to above
-//                            if (p.second.trim().length() > 0) {
-//                                int idx = p.second.lastIndexOf("/");
-//                                dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
-//                            }
-//                        }
-//                        if (pageCount > 0) {
-//                            dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
-//                                    lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
-//                                    db.getCachesize()));
-//                        }
-//                    }
-//                    // if there are pooled connections, return the cache stats for them also.
-//                    if (db.mConnectionPool != null) {
-//                        for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) {
-//                            dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
-//                                    + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
-//                                    pDb.getCacheMissNum(), pDb.getCachesize()));
-//                        }
-//                    }
-//                } catch (SQLiteException e) {
-//                    // ignore. we don't care about exceptions when we are taking adb
-//                    // bugreport!
-//                }
-//            }
-//        }
+        // make a local copy of mActiveDatabases - so that this method is not competing
+        // for synchronization lock on mActiveDatabases
+        ArrayList<WeakReference<SQLiteDatabase>> tempList;
+        synchronized(mActiveDatabases) {
+            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
+        }
+        for (WeakReference<SQLiteDatabase> w : tempList) {
+            SQLiteDatabase db = w.get();
+            if (db == null || !db.isOpen()) {
+                continue;
+            }
+
+            try {
+                // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
+                int lookasideUsed = db.native_getDbLookaside();
+
+                // get the lastnode of the dbname
+                String path = db.getPath();
+                int indx = path.lastIndexOf("/");
+                String lastnode = path.substring((indx != -1) ? ++indx : 0);
+
+                // get list of attached dbs and for each db, get its size and pagesize
+                ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
+                if (attachedDbs == null) {
+                    continue;
+                }
+                for (int i = 0; i < attachedDbs.size(); i++) {
+                    Pair<String, String> p = attachedDbs.get(i);
+                    long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
+                            + ".page_count;", null);
+
+                    // first entry in the attached db list is always the main database
+                    // don't worry about prefixing the dbname with "main"
+                    String dbName;
+                    if (i == 0) {
+                        dbName = lastnode;
+                    } else {
+                        // lookaside is only relevant for the main db
+                        lookasideUsed = 0;
+                        dbName = "  (attached) " + p.first;
+                        // if the attached db has a path, attach the lastnode from the path to above
+                        if (p.second.trim().length() > 0) {
+                            int idx = p.second.lastIndexOf("/");
+                            dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
+                        }
+                    }
+                    if (pageCount > 0) {
+                        dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
+                                lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
+                                db.getCachesize()));
+                    }
+                }
+                // if there are pooled connections, return the cache stats for them also.
+                // while we are trying to query the pooled connections for stats, some other thread
+                // could be disabling conneciton pool. so, grab a reference to the connection pool.
+                DatabaseConnectionPool connPool = db.mConnectionPool;
+                if (connPool != null) {
+                    for (SQLiteDatabase pDb : connPool.getConnectionList()) {
+                        dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
+                                + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
+                                pDb.getCacheMissNum(), pDb.getCachesize()));
+                    }
+                }
+            } catch (SQLiteException e) {
+                // ignore. we don't care about exceptions when we are taking adb
+                // bugreport!
+            }
+        }
         return dbStatsList;
     }
 
@@ -2510,6 +2523,20 @@
             return null;
         }
         ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
+        if (!mHasAttachedDbs) {
+            // No attached databases.
+            // There is a small window where attached databases exist but this flag is not set yet.
+            // This can occur when this thread is in a race condition with another thread
+            // that is executing the SQL statement: "attach database <blah> as <foo>"
+            // If this thread is NOT ok with such a race condition (and thus possibly not receive
+            // the entire list of attached databases), then the caller should ensure that no thread
+            // is executing any SQL statements while a thread is calling this method.
+            // Typically, this method is called when 'adb bugreport' is done or the caller wants to
+            // collect stats on the database and all its attached databases.
+            attachedDbs.add(new Pair<String, String>("main", mPath));
+            return attachedDbs;
+        }
+        // has attached databases. query sqlite to get the list of attached databases.
         Cursor c = null;
         try {
             c = rawQuery("pragma database_list;", null);
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 4747a9e..83621f2 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -25,7 +25,7 @@
 /**
  * A base class for compiled SQLite programs.
  *<p>
- * SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple
+ * SQLiteProgram is NOT internally synchronized so code using a SQLiteProgram from multiple
  * threads should perform its own synchronization when using the SQLiteProgram.
  */
 public abstract class SQLiteProgram extends SQLiteClosable {
@@ -180,25 +180,11 @@
         mDatabase.releaseReference();
     }
 
-    /* package */ synchronized void release() {
+    /* package */ void release() {
         if (mCompiledSql == null) {
             return;
         }
-        if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
-            // this SQL statement was never in cache
-            mCompiledSql.releaseSqlStatement();
-        } else {
-            synchronized(mDatabase.mCompiledQueries) {
-                if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
-                    // it is NOT in compiled-sql cache. i.e., responsibility of
-                    // releasing this statement is on me.
-                    mCompiledSql.releaseSqlStatement();
-                } else {
-                    // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
-                    mCompiledSql.release();
-                }
-            }
-        }
+        mDatabase.releaseCompiledSqlObj(mCompiledSql);
         mCompiledSql = null;
         nStatement = 0;
     }
@@ -241,34 +227,32 @@
     }
 
     private void bind(int type, int index, Object value) {
-        synchronized (this) {
-            mDatabase.verifyDbIsOpen();
-            addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value);
-            if (nStatement > 0) {
-                // bind only if the SQL statement is compiled
-                acquireReference();
-                try {
-                    switch (type) {
-                        case Cursor.FIELD_TYPE_NULL:
-                            native_bind_null(index);
-                            break;
-                        case Cursor.FIELD_TYPE_BLOB:
-                            native_bind_blob(index, (byte[]) value);
-                            break;
-                        case Cursor.FIELD_TYPE_FLOAT:
-                            native_bind_double(index, (Double) value);
-                            break;
-                        case Cursor.FIELD_TYPE_INTEGER:
-                            native_bind_long(index, (Long) value);
-                            break;
-                        case Cursor.FIELD_TYPE_STRING:
-                        default:
-                            native_bind_string(index, (String) value);
-                            break;
-                    }
-                } finally {
-                    releaseReference();
+        mDatabase.verifyDbIsOpen();
+        addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value);
+        if (nStatement > 0) {
+            // bind only if the SQL statement is compiled
+            acquireReference();
+            try {
+                switch (type) {
+                    case Cursor.FIELD_TYPE_NULL:
+                        native_bind_null(index);
+                        break;
+                    case Cursor.FIELD_TYPE_BLOB:
+                        native_bind_blob(index, (byte[]) value);
+                        break;
+                    case Cursor.FIELD_TYPE_FLOAT:
+                        native_bind_double(index, (Double) value);
+                        break;
+                    case Cursor.FIELD_TYPE_INTEGER:
+                        native_bind_long(index, (Long) value);
+                        break;
+                    case Cursor.FIELD_TYPE_STRING:
+                    default:
+                        native_bind_string(index, (String) value);
+                        break;
                 }
+            } finally {
+                releaseReference();
             }
         }
     }
@@ -337,18 +321,16 @@
      * Clears all existing bindings. Unset bindings are treated as NULL.
      */
     public void clearBindings() {
-        synchronized (this) {
-            mBindArgs = null;
-            if (this.nStatement == 0) {
-                return;
-            }
-            mDatabase.verifyDbIsOpen();
-            acquireReference();
-            try {
-                native_clear_bindings();
-            } finally {
-                releaseReference();
-            }
+        mBindArgs = null;
+        if (this.nStatement == 0) {
+            return;
+        }
+        mDatabase.verifyDbIsOpen();
+        acquireReference();
+        try {
+            native_clear_bindings();
+        } finally {
+            releaseReference();
         }
     }
 
@@ -356,23 +338,21 @@
      * Release this program's resources, making it invalid.
      */
     public void close() {
-        synchronized (this) {
-            mBindArgs = null;
-            if (nHandle == 0 || !mDatabase.isOpen()) {
-                return;
-            }
-            releaseReference();
+        mBindArgs = null;
+        if (nHandle == 0 || !mDatabase.isOpen()) {
+            return;
         }
+        releaseReference();
     }
 
-    private synchronized void addToBindArgs(int index, Object value) {
+    private void addToBindArgs(int index, Object value) {
         if (mBindArgs == null) {
             mBindArgs = new HashMap<Integer, Object>();
         }
         mBindArgs.put(index, value);
     }
 
-    /* package */ synchronized void compileAndbindAllArgs() {
+    /* package */ void compileAndbindAllArgs() {
         if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
             // no need to prepare this SQL statement
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
@@ -424,10 +404,8 @@
             return;
         }
         int size = bindArgs.length;
-        synchronized(this) {
-            for (int i = 0; i < size; i++) {
-                bindString(i + 1, bindArgs[i]);
-            }
+        for (int i = 0; i < size; i++) {
+            bindString(i + 1, bindArgs[i]);
         }
     }
 
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index bd05e24..5e96928 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -31,7 +31,7 @@
  * Don't use SQLiteStatement constructor directly, please use
  * {@link SQLiteDatabase#compileStatement(String)}
  *<p>
- * SQLiteStatement is not internally synchronized so code using a SQLiteStatement from multiple
+ * SQLiteStatement is NOT internally synchronized so code using a SQLiteStatement from multiple
  * threads should perform its own synchronization when using the SQLiteStatement.
  */
 @SuppressWarnings("deprecation")
@@ -79,23 +79,21 @@
      *         some reason
      */
     public int executeUpdateDelete() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(WRITE);
-                int numChanges = 0;
-                if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
-                    // since the statement doesn't have to be prepared,
-                    // call the following native method which will not prepare
-                    // the query plan
-                    native_executeSql(mSql);
-                } else {
-                    numChanges = native_execute();
-                }
-                mDatabase.logTimeStat(mSql, timeStart);
-                return numChanges;
-            } finally {
-                releaseAndUnlock();
+        try {
+            long timeStart = acquireAndLock(WRITE);
+            int numChanges = 0;
+            if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
+                // since the statement doesn't have to be prepared,
+                // call the following native method which will not prepare
+                // the query plan
+                native_executeSql(mSql);
+            } else {
+                numChanges = native_execute();
             }
+            mDatabase.logTimeStat(mSql, timeStart);
+            return numChanges;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -109,15 +107,13 @@
      *         some reason
      */
     public long executeInsert() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(WRITE);
-                long lastInsertedRowId = native_executeInsert();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return lastInsertedRowId;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(WRITE);
+            long lastInsertedRowId = native_executeInsert();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return lastInsertedRowId;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -130,15 +126,13 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public long simpleQueryForLong() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(READ);
-                long retValue = native_1x1_long();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return retValue;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(READ);
+            long retValue = native_1x1_long();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return retValue;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -151,15 +145,13 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public String simpleQueryForString() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(READ);
-                String retValue = native_1x1_string();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return retValue;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(READ);
+            String retValue = native_1x1_string();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return retValue;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
@@ -172,18 +164,16 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
-        synchronized(this) {
-            try {
-                long timeStart = acquireAndLock(READ);
-                ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
-                mDatabase.logTimeStat(mSql, timeStart);
-                return retValue;
-            } catch (IOException ex) {
-                Log.e(TAG, "simpleQueryForBlobFileDescriptor() failed", ex);
-                return null;
-            } finally {
-                releaseAndUnlock();
-            }
+        try {
+            long timeStart = acquireAndLock(READ);
+            ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return retValue;
+        } catch (IOException ex) {
+            Log.e(TAG, "simpleQueryForBlobFileDescriptor() failed", ex);
+            return null;
+        } finally {
+            releaseAndUnlock();
         }
     }
 
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index 140b71f..5fd0d89 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -88,13 +88,23 @@
      * @hide
      */
     public void writeToParcel(Parcel dest, int flags) {
+        String host = null;
         if (mProxy != null) {
-            InetAddress addr = mProxy.getAddress();
-            if (addr != null) {
-                dest.writeByte((byte)1);
-                dest.writeByteArray(addr.getAddress());
-                dest.writeInt(mProxy.getPort());
-            }
+            try {
+                InetAddress addr = mProxy.getAddress();
+                if (addr != null) {
+                    host = addr.getHostAddress();
+                } else {
+                    /* Does not resolve when addr is null */
+                    host = mProxy.getHostName();
+                }
+            } catch (Exception e) { }
+        }
+
+        if (host != null) {
+            dest.writeByte((byte)1);
+            dest.writeString(host);
+            dest.writeInt(mProxy.getPort());
         } else {
             dest.writeByte((byte)0);
         }
@@ -111,9 +121,11 @@
                 ProxyProperties proxyProperties = new ProxyProperties();
                 if (in.readByte() == 1) {
                     try {
-                        InetAddress addr = InetAddress.getByAddress(in.createByteArray());
-                        proxyProperties.setSocketAddress(new InetSocketAddress(addr, in.readInt()));
-                    } catch (UnknownHostException e) { }
+                        String host = in.readString();
+                        int port = in.readInt();
+                        proxyProperties.setSocketAddress(InetSocketAddress.createUnresolved(
+                                host, port));
+                    } catch (IllegalArgumentException e) { }
                 }
                 proxyProperties.setExclusionList(in.readString());
                 return proxyProperties;
diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java
new file mode 100644
index 0000000..a2d0a56
--- /dev/null
+++ b/core/java/android/os/storage/OnObbStateChangeListener.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Used for receiving notifications from {@link StorageManager}.
+ */
+public abstract class OnObbStateChangeListener {
+    /**
+     * Called when an OBB has changed states.
+     * 
+     * @param path path to the OBB file the state change has happened on
+     * @param state the current state of the OBB
+     */
+    public void onObbStateChange(String path, String state) {
+    }
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4a0296b..4268618 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -23,14 +23,28 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 
 /**
- * StorageManager is the interface to the systems storage service.
+ * StorageManager is the interface to the systems storage service. The storage
+ * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
+ * <p>
+ * OBBs contain a filesystem that maybe be encrypted on disk and mounted
+ * on-demand from an application. OBBs are a good way of providing large amounts
+ * of binary assets without packaging them into APKs as they may be multiple
+ * gigabytes in size. However, due to their size, they're most likely stored in
+ * a shared storage pool accessible from all programs. The system does not
+ * guarantee the security of the OBB file itself: if any program modifies the
+ * OBB, there is no guarantee that a read from that OBB will produce the
+ * expected output.
+ * <p>
  * Get an instance of this class by calling
- * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
- * of {@link android.content.Context#STORAGE_SERVICE}.
- *
+ * {@link android.content.Context#getSystemService(java.lang.String)} with an
+ * argument of {@link android.content.Context#STORAGE_SERVICE}.
  */
 
 public class StorageManager
@@ -76,11 +90,113 @@
     /**
      * Binder listener for OBB action results.
      */
-    private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
-    private class ObbActionBinderListener extends IObbActionListener.Stub {
+    private final ObbActionListener mObbActionListener = new ObbActionListener();
+
+    private class ObbActionListener extends IObbActionListener.Stub {
+        private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>();
+
         @Override
         public void onObbResult(String filename, String status) throws RemoteException {
-            Log.i(TAG, "filename = " + filename + ", result = " + status);
+            synchronized (mListeners) {
+                final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
+                while (iter.hasNext()) {
+                    final WeakReference<ObbListenerDelegate> ref = iter.next();
+
+                    final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
+                    if (delegate == null) {
+                        iter.remove();
+                        continue;
+                    }
+
+                    delegate.sendObbStateChanged(filename, status);
+                }
+            }
+        }
+
+        public void addListener(OnObbStateChangeListener listener) {
+            if (listener == null) {
+                return;
+            }
+
+            synchronized (mListeners) {
+                final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
+                while (iter.hasNext()) {
+                    final WeakReference<ObbListenerDelegate> ref = iter.next();
+
+                    final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
+                    if (delegate == null) {
+                        iter.remove();
+                        continue;
+                    }
+
+                    /*
+                     * If we're already in the listeners, we don't need to be in
+                     * there again.
+                     */
+                    if (listener.equals(delegate.getListener())) {
+                        return;
+                    }
+                }
+
+                final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
+                mListeners.add(new WeakReference<ObbListenerDelegate>(delegate));
+            }
+        }
+    }
+
+    /**
+     * Private class containing sender and receiver code for StorageEvents.
+     */
+    private class ObbListenerDelegate {
+        private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
+        private final Handler mHandler;
+
+        ObbListenerDelegate(OnObbStateChangeListener listener) {
+            mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
+            mHandler = new Handler(mTgtLooper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    final OnObbStateChangeListener listener = getListener();
+                    if (listener == null) {
+                        return;
+                    }
+
+                    StorageEvent e = (StorageEvent) msg.obj;
+
+                    if (msg.what == StorageEvent.EVENT_OBB_STATE_CHANGED) {
+                        ObbStateChangedStorageEvent ev = (ObbStateChangedStorageEvent) e;
+                        listener.onObbStateChange(ev.path, ev.state);
+                    } else {
+                        Log.e(TAG, "Unsupported event " + msg.what);
+                    }
+                }
+            };
+        }
+
+        OnObbStateChangeListener getListener() {
+            if (mObbEventListenerRef == null) {
+                return null;
+            }
+            return mObbEventListenerRef.get();
+        }
+
+        void sendObbStateChanged(String path, String state) {
+            ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state);
+            mHandler.sendMessage(e.getMessage());
+        }
+    }
+
+    /**
+     * Message sent during an OBB status change event.
+     */
+    private class ObbStateChangedStorageEvent extends StorageEvent {
+        public final String path;
+        public final String state;
+
+        public ObbStateChangedStorageEvent(String path, String state) {
+            super(EVENT_OBB_STATE_CHANGED);
+            this.path = path;
+            this.state = state;
         }
     }
 
@@ -89,8 +205,9 @@
      * and the target looper handler.
      */
     private class StorageEvent {
-        public static final int EVENT_UMS_CONNECTION_CHANGED = 1;
-        public static final int EVENT_STORAGE_STATE_CHANGED   = 2;
+        static final int EVENT_UMS_CONNECTION_CHANGED = 1;
+        static final int EVENT_STORAGE_STATE_CHANGED = 2;
+        static final int EVENT_OBB_STATE_CHANGED = 3;
 
         private Message mMessage;
 
@@ -291,19 +408,27 @@
      * specified, it is supplied to the mounting process to be used in any
      * encryption used in the OBB.
      * <p>
+     * The OBB will remain mounted for as long as the StorageManager reference
+     * is held by the application. As soon as this reference is lost, the OBBs
+     * in use will be unmounted. The {@link OnObbStateChangeListener} registered with
+     * this call will receive all further OBB-related events until it goes out
+     * of scope. If the caller is not interested in whether the call succeeds,
+     * the <code>listener</code> may be specified as <code>null</code>.
+     * <p>
      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
      * file matches a package ID that is owned by the calling program's UID.
-     * That is, shared UID applications can obtain access to any other
+     * That is, shared UID applications can attempt to mount any other
      * application's OBB that shares its UID.
-     * <p>
-     * STOPSHIP document more; discuss lack of guarantees of security
      * 
      * @param filename the path to the OBB file
-     * @param key decryption key
+     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
+     *            encryption was used on the OBB.
      * @return whether the mount call was successfully queued or not
+     * @throws IllegalArgumentException when the OBB is already mounted
      */
-    public boolean mountObb(String filename, String key) {
+    public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
         try {
+            mObbActionListener.addListener(listener);
             mMountService.mountObb(filename, key, mObbActionListener);
             return true;
         } catch (RemoteException e) {
@@ -314,15 +439,20 @@
     }
 
     /**
-     * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
-     * is true, it will kill any application needed to unmount the given OBB.
+     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
+     * <code>force</code> flag is true, it will kill any application needed to
+     * unmount the given OBB (even the calling application).
+     * <p>
+     * The {@link OnObbStateChangeListener} registered with this call will receive all
+     * further OBB-related events until it goes out of scope. If the caller is
+     * not interested in whether the call succeeded, the listener may be
+     * specified as <code>null</code>.
      * <p>
      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
      * file matches a package ID that is owned by the calling program's UID.
      * That is, shared UID applications can obtain access to any other
      * application's OBB that shares its UID.
      * <p>
-     * STOPSHIP document more; discuss lack of guarantees of security
      * 
      * @param filename path to the OBB file
      * @param force whether to kill any programs using this in order to unmount
@@ -330,8 +460,10 @@
      * @return whether the unmount call was successfully queued or not
      * @throws IllegalArgumentException when OBB is not already mounted
      */
-    public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
+    public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener)
+            throws IllegalArgumentException {
         try {
+            mObbActionListener.addListener(listener);
             mMountService.unmountObb(filename, force, mObbActionListener);
             return true;
         } catch (RemoteException e) {
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
index 276bddc..03bc41a 100644
--- a/core/java/android/provider/BrowserContract.java
+++ b/core/java/android/provider/BrowserContract.java
@@ -21,10 +21,12 @@
 import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
 import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.provider.SyncStateContract;
 import android.util.Pair;
 
 /**
@@ -493,4 +495,61 @@
          */
         public static final String IS_BOOKMARK = "bookmark";
     }
+
+    /**
+     * A table that stores settings specific to the browser. Only support query and insert.
+     */
+    public static final class Settings {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Settings() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "settings");
+
+        /**
+         * Key for a setting value.
+         */
+        public static final String KEY = "key";
+
+        /**
+         * Value for a setting.
+         */
+        public static final String VALUE = "value";
+
+        /**
+         * If set to non-0 the user has opted into bookmark sync.
+         */
+        public static final String KEY_SYNC_ENABLED = "sync_enabled";
+
+        /**
+         * Returns true if bookmark sync is enabled
+         */
+        static public boolean isSyncEnabled(Context context) {
+            Cursor cursor = null;
+            try {
+                cursor = context.getContentResolver().query(CONTENT_URI, new String[] { VALUE },
+                        KEY + "=?", new String[] { KEY_SYNC_ENABLED }, null);
+                if (cursor == null || !cursor.moveToFirst()) {
+                    return false;
+                }
+                return cursor.getInt(0) != 0;
+            } finally {
+                if (cursor != null) cursor.close();
+            }
+        }
+
+        /**
+         * Sets the bookmark sync enabled setting.
+         */
+        static public void setSyncEnabled(Context context, boolean enabled) {
+            ContentValues values = new ContentValues();
+            values.put(KEY, KEY_SYNC_ENABLED);
+            values.put(VALUE, enabled ? 1 : 0);
+            context.getContentResolver().insert(CONTENT_URI, values);
+        }
+    }
 }
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index da02845..b74e76f 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1234,6 +1234,19 @@
             }
 
             /**
+             * Get the content:// style URI for querying the genres of an audio file.
+             *
+             * @param volumeName the name of the volume to get the URI for
+             * @param audioId the ID of the audio file for which to retrieve the genres
+             * @return the URI to for querying the genres for the audio file
+             * with the given the volume and audioID
+             */
+            public static Uri getContentUriForAudioId(String volumeName, int audioId) {
+                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
+                        "/audio/media/" + audioId + "/genres");
+            }
+
+            /**
              * The content:// style URI for the internal storage.
              */
             public static final Uri INTERNAL_CONTENT_URI =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e98fa26..cf95872 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -350,6 +350,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
@@ -1628,6 +1642,86 @@
         public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
 
         /**
+         * Whether nfc is enabled/disabled
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_ON = "nfc_on";
+
+        /**
+         * Whether nfc secure element is enabled/disabled
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_SECURE_ELEMENT_ON = "nfc_secure_element_on";
+
+        /**
+         * Whether nfc secure element is enabled/disabled
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_SECURE_ELEMENT_ID = "nfc_secure_element_id";
+
+        /**
+         * LLCP LTO value
+         * @hide
+         */
+        public static final String NFC_LLCP_LTO = "nfc_llcp_lto";
+
+        /**
+         * LLCP MIU value
+         * @hide
+         */
+        public static final String NFC_LLCP_MIU = "nfc_llcp_miu";
+
+        /**
+         * LLCP WKS value
+         * @hide
+         */
+        public static final String NFC_LLCP_WKS = "nfc_llcp_wks";
+
+        /**
+         * LLCP OPT value
+         * @hide
+         */
+        public static final String NFC_LLCP_OPT = "nfc_llcp_opt";
+
+        /**
+         * NFC Discovery Reader A
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_DISCOVERY_A = "nfc_discovery_a";
+
+        /**
+         * NFC Discovery Reader B
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_DISCOVERY_B = "nfc_discovery_b";
+
+        /**
+         * NFC Discovery Reader Felica
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_DISCOVERY_F = "nfc_discovery_felica";
+
+        /**
+         * NFC Discovery Reader 15693
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_DISCOVERY_15693 = "nfc_discovery_15693";
+
+        /**
+         * NFC Discovery NFCIP
+         * 0=disabled. 1=enabled.
+         * @hide
+         */
+        public static final String NFC_DISCOVERY_NFCIP = "nfc_discovery_nfcip";
+
+        /**
          * Show pointer location on screen?
          * 0 = no
          * 1 = yes
@@ -1800,7 +1894,19 @@
             NOTIFICATION_LIGHT_PULSE,
             USE_PTP_INTERFACE,
             SIP_CALL_OPTIONS,
-            SIP_RECEIVE_CALLS
+            SIP_RECEIVE_CALLS,
+            NFC_ON,
+            NFC_SECURE_ELEMENT_ON,
+            NFC_SECURE_ELEMENT_ID,
+            NFC_LLCP_LTO,
+            NFC_LLCP_MIU,
+            NFC_LLCP_WKS,
+            NFC_LLCP_OPT,
+            NFC_DISCOVERY_A,
+            NFC_DISCOVERY_B,
+            NFC_DISCOVERY_F,
+            NFC_DISCOVERY_15693,
+            NFC_DISCOVERY_NFCIP,
         };
 
         // Settings moved to Settings.Secure
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index a52a221..946c266 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -25,6 +25,7 @@
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothA2dp;
 import android.content.BroadcastReceiver;
@@ -32,17 +33,15 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioManager;
-import android.os.Handler;
-import android.os.Message;
 import android.os.ParcelUuid;
 import android.provider.Settings;
 import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
+
 
 public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
     private static final String TAG = "BluetoothA2dpService";
@@ -57,11 +56,6 @@
 
     private static final String PROPERTY_STATE = "State";
 
-    private static final String SINK_STATE_DISCONNECTED = "disconnected";
-    private static final String SINK_STATE_CONNECTING = "connecting";
-    private static final String SINK_STATE_CONNECTED = "connected";
-    private static final String SINK_STATE_PLAYING = "playing";
-
     private static int mSinkCount;
 
     private final Context mContext;
@@ -72,6 +66,7 @@
     private final BluetoothAdapter mAdapter;
     private int   mTargetA2dpState;
     private boolean mAdjustedPriority = false;
+    private BluetoothDevice mPlayingA2dpDevice;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -95,12 +90,12 @@
                                                    BluetoothDevice.ERROR);
                 switch(bondState) {
                 case BluetoothDevice.BOND_BONDED:
-                    if (getSinkPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
-                        setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
+                    if (getPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
+                        setPriority(device, BluetoothA2dp.PRIORITY_ON);
                     }
                     break;
                 case BluetoothDevice.BOND_NONE:
-                    setSinkPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
+                    setPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
                     break;
                 }
             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
@@ -113,7 +108,8 @@
             } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
                 if (streamType == AudioManager.STREAM_MUSIC) {
-                    BluetoothDevice sinks[] = getConnectedSinks();
+                    BluetoothDevice sinks[] = getConnectedDevices();
+
                     if (sinks.length != 0 && isPhoneDocked(sinks[0])) {
                         String address = sinks[0].getAddress();
                         int newVolLevel =
@@ -254,7 +250,7 @@
             BluetoothDevice[] devices = new BluetoothDevice[mAudioDevices.size()];
             devices = mAudioDevices.keySet().toArray(devices);
             for (BluetoothDevice device : devices) {
-                int state = getSinkState(device);
+                int state = getConnectionState(device);
                 switch (state) {
                     case BluetoothA2dp.STATE_CONNECTING:
                     case BluetoothA2dp.STATE_CONNECTED:
@@ -277,7 +273,7 @@
 
     private synchronized boolean isConnectSinkFeasible(BluetoothDevice device) {
         if (!mBluetoothService.isEnabled() || !isSinkDevice(device) ||
-                getSinkPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
+                getPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
                 return false;
             }
 
@@ -292,12 +288,26 @@
             return true;
     }
 
-    public synchronized boolean connectSink(BluetoothDevice device) {
+    public synchronized boolean isA2dpPlaying(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+            "Need BLUETOOTH_ADMIN permission");
+        if (DBG) log("isA2dpPlaying(" + device + ")");
+        if (device.equals(mPlayingA2dpDevice)) return true;
+        return false;
+    }
+
+    public synchronized boolean connect(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
         if (DBG) log("connectSink(" + device + ")");
         if (!isConnectSinkFeasible(device)) return false;
 
+        for (BluetoothDevice sinkDevice : mAudioDevices.keySet()) {
+            if (getConnectionState(sinkDevice) != BluetoothProfile.STATE_DISCONNECTED) {
+                disconnect(sinkDevice);
+            }
+        }
+
         return mBluetoothService.connectSink(device.getAddress());
     }
 
@@ -307,17 +317,15 @@
         int state = mAudioDevices.get(device);
 
         // ignore if there are any active sinks
-        if (lookupSinksMatchingStates(new int[] {
+        if (getDevicesMatchingConnectionStates(new int[] {
                 BluetoothA2dp.STATE_CONNECTING,
                 BluetoothA2dp.STATE_CONNECTED,
-                BluetoothA2dp.STATE_PLAYING,
-                BluetoothA2dp.STATE_DISCONNECTING}).size() != 0) {
+                BluetoothA2dp.STATE_DISCONNECTING}).length != 0) {
             return false;
         }
 
         switch (state) {
         case BluetoothA2dp.STATE_CONNECTED:
-        case BluetoothA2dp.STATE_PLAYING:
         case BluetoothA2dp.STATE_DISCONNECTING:
             return false;
         case BluetoothA2dp.STATE_CONNECTING:
@@ -343,17 +351,16 @@
             return false;
         }
 
-        int state = getSinkState(device);
+        int state = getConnectionState(device);
         switch (state) {
         case BluetoothA2dp.STATE_DISCONNECTED:
-            return false;
         case BluetoothA2dp.STATE_DISCONNECTING:
-            return true;
+            return false;
         }
         return true;
     }
 
-    public synchronized boolean disconnectSink(BluetoothDevice device) {
+    public synchronized boolean disconnect(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
         if (DBG) log("disconnectSink(" + device + ")");
@@ -362,7 +369,7 @@
     }
 
     public synchronized boolean disconnectSinkInternal(BluetoothDevice device) {
-        int state = getSinkState(device);
+        int state = getConnectionState(device);
         String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
 
         // State is CONNECTING or CONNECTED or PLAYING
@@ -408,24 +415,7 @@
         return checkSinkSuspendState(state.intValue());
     }
 
-    public synchronized BluetoothDevice[] getConnectedSinks() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        Set<BluetoothDevice> sinks = lookupSinksMatchingStates(
-                new int[] {BluetoothA2dp.STATE_CONNECTED, BluetoothA2dp.STATE_PLAYING});
-        return sinks.toArray(new BluetoothDevice[sinks.size()]);
-    }
-
-    public synchronized BluetoothDevice[] getNonDisconnectedSinks() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        Set<BluetoothDevice> sinks = lookupSinksMatchingStates(
-                new int[] {BluetoothA2dp.STATE_CONNECTED,
-                           BluetoothA2dp.STATE_PLAYING,
-                           BluetoothA2dp.STATE_CONNECTING,
-                           BluetoothA2dp.STATE_DISCONNECTING});
-        return sinks.toArray(new BluetoothDevice[sinks.size()]);
-    }
-
-    public synchronized int getSinkState(BluetoothDevice device) {
+    public synchronized int getConnectionState(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         Integer state = mAudioDevices.get(device);
         if (state == null)
@@ -433,19 +423,41 @@
         return state;
     }
 
-    public synchronized int getSinkPriority(BluetoothDevice device) {
+    public synchronized BluetoothDevice[] getConnectedDevices() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        BluetoothDevice[] sinks = getDevicesMatchingConnectionStates(
+                new int[] {BluetoothA2dp.STATE_CONNECTED});
+        return sinks;
+    }
+
+    public synchronized BluetoothDevice[] getDevicesMatchingConnectionStates(int[] states) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        ArrayList<BluetoothDevice> sinks = new ArrayList();
+        if (mAudioDevices.isEmpty()) {
+            return sinks.toArray(new BluetoothDevice[sinks.size()]);
+        }
+        for (BluetoothDevice device: mAudioDevices.keySet()) {
+            int sinkState = getConnectionState(device);
+            for (int state : states) {
+                if (state == sinkState) {
+                    sinks.add(device);
+                    break;
+                }
+            }
+        }
+        return sinks.toArray(new BluetoothDevice[sinks.size()]);
+    }
+
+    public synchronized int getPriority(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         return Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
                 BluetoothA2dp.PRIORITY_UNDEFINED);
     }
 
-    public synchronized boolean setSinkPriority(BluetoothDevice device, int priority) {
+    public synchronized boolean setPriority(BluetoothDevice device, int priority) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
-        if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
-            return false;
-        }
         return Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
     }
@@ -471,8 +483,17 @@
                 // We have authorized it and bluez state has changed.
                 addAudioSink(device);
             } else {
-                int prevState = mAudioDevices.get(device);
-                handleSinkStateChange(device, prevState, state);
+                if (state == BluetoothA2dp.STATE_PLAYING && mPlayingA2dpDevice == null) {
+                   mPlayingA2dpDevice = device;
+                   handleSinkPlayingStateChange(device, state, BluetoothA2dp.STATE_NOT_PLAYING);
+                } else if (state == BluetoothA2dp.STATE_CONNECTED && mPlayingA2dpDevice != null) {
+                    mPlayingA2dpDevice = null;
+                    handleSinkPlayingStateChange(device, BluetoothA2dp.STATE_NOT_PLAYING,
+                        BluetoothA2dp.STATE_PLAYING);
+                } else {
+                   int prevState = mAudioDevices.get(device);
+                   handleSinkStateChange(device, prevState, state);
+                }
             }
         }
     }
@@ -484,18 +505,19 @@
                 mSinkCount--;
             } else if (state == BluetoothA2dp.STATE_CONNECTED) {
                 mSinkCount ++;
+                mPlayingA2dpDevice = null;
             }
             mAudioDevices.put(device, state);
 
             checkSinkSuspendState(state);
             mTargetA2dpState = -1;
 
-            if (getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
+            if (getPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
                     state == BluetoothA2dp.STATE_CONNECTING ||
                     state == BluetoothA2dp.STATE_CONNECTED) {
                 // We have connected or attempting to connect.
                 // Bump priority
-                setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
+                setPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
             }
 
             if (state == BluetoothA2dp.STATE_CONNECTED) {
@@ -504,45 +526,38 @@
                 adjustOtherSinkPriorities(device);
             }
 
-            Intent intent = new Intent(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+            Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-            intent.putExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, prevState);
-            intent.putExtra(BluetoothA2dp.EXTRA_SINK_STATE, state);
+            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+            intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
 
             if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
         }
     }
 
+    private void handleSinkPlayingStateChange(BluetoothDevice device, int state, int prevState) {
+        Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
+        if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
+    }
+
     private void adjustOtherSinkPriorities(BluetoothDevice connectedDevice) {
         if (!mAdjustedPriority) {
             for (BluetoothDevice device : mAdapter.getBondedDevices()) {
-                if (getSinkPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
+                if (getPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
                     !device.equals(connectedDevice)) {
-                    setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
+                    setPriority(device, BluetoothA2dp.PRIORITY_ON);
                 }
             }
             mAdjustedPriority = true;
         }
     }
 
-    private synchronized Set<BluetoothDevice> lookupSinksMatchingStates(int[] states) {
-        Set<BluetoothDevice> sinks = new HashSet<BluetoothDevice>();
-        if (mAudioDevices.isEmpty()) {
-            return sinks;
-        }
-        for (BluetoothDevice device: mAudioDevices.keySet()) {
-            int sinkState = getSinkState(device);
-            for (int state : states) {
-                if (state == sinkState) {
-                    sinks.add(device);
-                    break;
-                }
-            }
-        }
-        return sinks;
-    }
-
     private boolean checkSinkSuspendState(int state) {
         boolean result = true;
 
@@ -568,7 +583,7 @@
                 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
                 if (address == null) return;
                 BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                int state = getSinkState(device);
+                int state = getConnectionState(device);
                 handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
             }
         }
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index ab79aaf..05cbeff 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothInputDevice;
 import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
 import android.content.Intent;
@@ -54,6 +55,7 @@
     private final HashMap<String, Integer> mPasskeyAgentRequestData;
     private final BluetoothService mBluetoothService;
     private final BluetoothAdapter mAdapter;
+    private BluetoothA2dp mA2dp;
     private final Context mContext;
     // The WakeLock is used for bringing up the LCD during a pairing request
     // from remote device when Android is in Suspend state.
@@ -118,8 +120,21 @@
                 | PowerManager.ON_AFTER_RELEASE, TAG);
         mWakeLock.setReferenceCounted(false);
         initializeNativeDataNative();
+
+        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
     }
 
+    private BluetoothProfile.ServiceListener mProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mA2dp = (BluetoothA2dp) proxy;
+        }
+        public void onServiceDisconnected(int profile) {
+            mA2dp = null;
+        }
+    };
+
+
     protected void finalize() throws Throwable {
         try {
             cleanupNativeDataNative();
@@ -574,12 +589,11 @@
 
         // Bluez sends the UUID of the local service being accessed, _not_ the
         // remote service
-        if ((BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
+        if (mA2dp != null &&
+            (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
               || BluetoothUuid.isAdvAudioDist(uuid)) &&
-              !isOtherSinkInNonDisconnectingState(address)) {
-            BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-
-            authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
+              !isOtherSinkInNonDisconnectedState(address)) {
+            authorized = mA2dp.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
             if (authorized) {
                 Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
                 mBluetoothService.notifyIncomingA2dpConnection(address);
@@ -630,9 +644,12 @@
         return false;
     }
 
-    private boolean isOtherSinkInNonDisconnectingState(String address) {
-        BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-        Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
+    private boolean isOtherSinkInNonDisconnectedState(String address) {
+        Set<BluetoothDevice> devices =
+            mA2dp.getDevicesMatchingConnectionStates(new int[] {BluetoothA2dp.STATE_CONNECTED,
+                                                     BluetoothA2dp.STATE_CONNECTING,
+                                                     BluetoothA2dp.STATE_DISCONNECTING});
+
         if (devices.size() == 0) return false;
         for(BluetoothDevice dev: devices) {
             if (!dev.getAddress().equals(address)) return true;
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 7f160c4..bd105a7 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -30,6 +30,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothDeviceProfileState;
 import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfileState;
 import android.bluetooth.BluetoothInputDevice;
 import android.bluetooth.BluetoothSocket;
@@ -88,6 +89,7 @@
 
     private int mNativeData;
     private BluetoothEventLoop mEventLoop;
+    private BluetoothHeadset mBluetoothHeadset;
     private boolean mIsAirplaneSensitive;
     private boolean mIsAirplaneToggleable;
     private int mBluetoothState;
@@ -2434,7 +2436,8 @@
         pw.println("Local name = " + getName());
         pw.println("isDiscovering() = " + isDiscovering());
 
-        BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
+        mAdapter.getProfileProxy(mContext,
+                                 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
 
         pw.println("\n--Known devices--");
         for (String address : mDeviceProperties.keySet()) {
@@ -2479,24 +2482,48 @@
         // Rather not do this from here, but no-where else and I need this
         // dump
         pw.println("\n--Headset Service--");
-        switch (headset.getState(headset.getCurrentHeadset())) {
-        case BluetoothHeadset.STATE_DISCONNECTED:
-            pw.println("getState() = STATE_DISCONNECTED");
-            break;
-        case BluetoothHeadset.STATE_CONNECTING:
-            pw.println("getState() = STATE_CONNECTING");
-            break;
-        case BluetoothHeadset.STATE_CONNECTED:
-            pw.println("getState() = STATE_CONNECTED");
-            break;
-        case BluetoothHeadset.STATE_ERROR:
-            pw.println("getState() = STATE_ERROR");
-            break;
+        if (mBluetoothHeadset != null) {
+           Set<BluetoothDevice> deviceSet = mBluetoothHeadset.getConnectedDevices();
+           if (deviceSet.size() == 0) {
+              pw.println("\n--No headsets connected--");
+           }
+           BluetoothDevice device = (BluetoothDevice) deviceSet.toArray()[0];
+
+            switch (mBluetoothHeadset.getConnectionState(device)) {
+                case BluetoothHeadset.STATE_DISCONNECTED:
+                    pw.println("getConnectionState() = STATE_DISCONNECTED");
+                    break;
+                case BluetoothHeadset.STATE_CONNECTING:
+                    pw.println("getConnectionState() = STATE_CONNECTING");
+                    break;
+                case BluetoothHeadset.STATE_CONNECTED:
+                    pw.println("getConnectionState() = STATE_CONNECTED");
+                    break;
+                case BluetoothHeadset.STATE_DISCONNECTING:
+                    pw.println("getConnectionState() = STATE_DISCONNECTING");
+                    break;
+                case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+                    pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
+                    break;
+            }
+
+            deviceSet.clear();
+            deviceSet = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
+                     BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
+            pw.println("\n--Connected and Disconnected Headsets");
+            for (BluetoothDevice dev: deviceSet) {
+                pw.println(device);
+                if (mBluetoothHeadset.isAudioConnected(device)) {
+                    pw.println("SCO audio connected to device:" + device);
+                }
+            }
+
+            pw.println("\ngetCurrentHeadset() = " + device);
+            pw.println("getBatteryUsageHint() = " +
+                       mBluetoothHeadset.getBatteryUsageHint(device));
+            mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
         }
 
-        pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
-        pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
-        headset.close();
         pw.println("\n--Application Service Records--");
         for (Integer handle : mServiceRecordToPid.keySet()) {
             Integer pid = mServiceRecordToPid.get(handle);
@@ -2504,6 +2531,16 @@
         }
     }
 
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mBluetoothHeadset = (BluetoothHeadset) proxy;
+    }
+        public void onServiceDisconnected(int profile) {
+            mBluetoothHeadset = null;
+        }
+    };
+
     /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
         if (pairable && discoverable)
             return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
@@ -2563,6 +2600,8 @@
     }
 
     public boolean connectHeadset(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
@@ -2575,6 +2614,8 @@
     }
 
     public boolean disconnectHeadset(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
@@ -2587,6 +2628,8 @@
     }
 
     public boolean connectSink(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
@@ -2599,6 +2642,8 @@
     }
 
     public boolean disconnectSink(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 733b535..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,49 +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
-     *
-     * @hide
-     */
-    public void setCursorController(CursorController cursorController) {
-        mCursorController = cursorController;
-    }
-
     public boolean canSelectArbitrarily() {
         return true;
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c147b74..dc3b44d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -590,6 +590,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/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 35cfbcb..efe4b9d 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -74,7 +74,8 @@
     // queue has been cleared,they are ignored.
     private boolean mBlockMessages = false;
 
-    private static String sDataDirectory = "";
+    private static String sDatabaseDirectory;
+    private static String sCacheDirectory;
 
     // Is this frame the main frame?
     private boolean mIsMainFrame;
@@ -228,9 +229,11 @@
         AssetManager am = context.getAssets();
         nativeCreateFrame(w, am, proxy.getBackForwardList());
 
-        if (sDataDirectory.length() == 0) {
-            String dir = appContext.getFilesDir().getAbsolutePath();
-            sDataDirectory =  dir.substring(0, dir.lastIndexOf('/'));
+        if (sDatabaseDirectory == null) {
+            sDatabaseDirectory = appContext.getDatabasePath("dummy").getParent();
+        }
+        if (sCacheDirectory == null) {
+            sCacheDirectory = appContext.getCacheDir().getAbsolutePath();
         }
 
         if (DebugFlags.BROWSER_FRAME) {
@@ -652,11 +655,19 @@
     }
 
     /**
-     * Called by JNI. Gets the applications data directory
-     * @return String The applications data directory
+     * Called by JNI. Gets the application's database directory, excluding the trailing slash.
+     * @return String The application's database directory
      */
-    private static String getDataDirectory() {
-        return sDataDirectory;
+    private static String getDatabaseDirectory() {
+        return sDatabaseDirectory;
+    }
+
+    /**
+     * Called by JNI. Gets the application's cache directory, excluding the trailing slash.
+     * @return String The application's cache directory
+     */
+    private static String getCacheDirectory() {
+        return sCacheDirectory;
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index bca9b36..7944807 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -457,6 +457,9 @@
     // default is not set, the UI will continue handle them.
     private boolean mDeferTouchProcess;
 
+    // if true, multi-touch events will be passed to webkit directly before UI
+    private boolean mDeferMultitouch = false;
+
     // to avoid interfering with the current touch events, track them
     // separately. Currently no snapping or fling in the deferred process mode
     private int mDeferTouchMode = TOUCH_DONE_MODE;
@@ -604,7 +607,7 @@
     static final int HIDE_FULLSCREEN                    = 121;
     static final int DOM_FOCUS_CHANGED                  = 122;
     static final int REPLACE_BASE_CONTENT               = 123;
-    // 124;
+    static final int FORM_DID_BLUR                      = 124;
     static final int RETURN_LABEL                       = 125;
     static final int FIND_AGAIN                         = 126;
     static final int CENTER_FIT_RECT                    = 127;
@@ -656,7 +659,7 @@
         "HIDE_FULLSCREEN", //                = 121;
         "DOM_FOCUS_CHANGED", //              = 122;
         "REPLACE_BASE_CONTENT", //           = 123;
-        "124", //                            = 124;
+        "FORM_DID_BLUR", //                  = 124;
         "RETURN_LABEL", //                   = 125;
         "FIND_AGAIN", //                     = 126;
         "CENTER_FIT_RECT", //                = 127;
@@ -1872,10 +1875,18 @@
      * @hide pending API council approval.
      */
     public static boolean cleanupPrivateBrowsingFiles(Context context) {
-        return nativeCleanupPrivateBrowsingFiles(context.getFilesDir().getParent());
+        // It seems wrong that we have to pass the storage locations here, given
+        // that the storage files are created native-side in WebRequestContext
+        // (albeit using a dumb getter on BrowserFrame to get the paths from
+        // Java). It looks like this is required because we may need to call
+        // this method before the BrowserFrame has been set up.
+        // TODO: Investigate whether this can be avoided.
+        return nativeCleanupPrivateBrowsingFiles(context.getDatabasePath("dummy").getParent(),
+                                                 context.getCacheDir().getAbsolutePath());
     }
 
-    private static native boolean nativeCleanupPrivateBrowsingFiles(String dataDirectory);
+    private static native boolean nativeCleanupPrivateBrowsingFiles(String databaseDirectory,
+                                                                    String cacheDirectory);
 
     private boolean extendScroll(int y) {
         int finalY = mScroller.getFinalY();
@@ -3843,10 +3854,9 @@
 
     // Called by WebKit to instruct the UI to hide the keyboard
     private void hideSoftKeyboard() {
-        InputMethodManager imm = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-        if (imm.isActive(this)
-                || (inEditingMode() && imm.isActive(mWebTextView))) {
+        InputMethodManager imm = InputMethodManager.peekInstance();
+        if (imm != null && (imm.isActive(this)
+                || (inEditingMode() && imm.isActive(mWebTextView)))) {
             imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
         }
     }
@@ -4938,13 +4948,14 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
+        if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
             return false;
         }
 
         if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
-                    + mTouchMode);
+            Log.v(LOGTAG, ev + " at " + ev.getEventTime()
+                + " mTouchMode=" + mTouchMode
+                + " numPointers=" + ev.getPointerCount());
         }
 
         int action = ev.getAction();
@@ -4991,16 +5002,18 @@
             }
         }
 
-        // FIXME: we may consider to give WebKit an option to handle multi-touch
-        // events later.
+        // If the page disallows zoom, pass multi-pointer events to webkit.
+        if (ev.getPointerCount() > 1
+            && (mZoomManager.isZoomScaleFixed() || mDeferMultitouch)) {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "passing " + ev.getPointerCount() + " points to webkit");
+            }
+            passMultiTouchToWebKit(ev);
+            return true;
+        }
+
         if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1 &&
                 mTouchMode != TOUCH_DRAG_LAYER_MODE && !skipScaleGesture) {
-
-            // if the page disallows zoom, skip multi-pointer action
-            if (!mZoomManager.supportsPanDuringZoom() && mZoomManager.isZoomScaleFixed()) {
-                return true;
-            }
-
             if (!detector.isInProgress() &&
                     ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) {
                 // Insert a fake pointer down event in order to start
@@ -5151,8 +5164,8 @@
                     if (shouldForwardTouchEvent()) {
                         TouchEventData ted = new TouchEventData();
                         ted.mAction = action;
-                        ted.mX = contentX;
-                        ted.mY = contentY;
+                        ted.mPoints = new Point[1];
+                        ted.mPoints[0] = new Point(contentX, contentY);
                         ted.mMetaState = ev.getMetaState();
                         ted.mReprocess = mDeferTouchProcess;
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
@@ -5193,8 +5206,8 @@
                         || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
                     TouchEventData ted = new TouchEventData();
                     ted.mAction = action;
-                    ted.mX = contentX;
-                    ted.mY = contentY;
+                    ted.mPoints = new Point[1];
+                    ted.mPoints[0] = new Point(contentX, contentY);
                     ted.mMetaState = ev.getMetaState();
                     ted.mReprocess = mDeferTouchProcess;
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
@@ -5382,8 +5395,8 @@
                 if (shouldForwardTouchEvent()) {
                     TouchEventData ted = new TouchEventData();
                     ted.mAction = action;
-                    ted.mX = contentX;
-                    ted.mY = contentY;
+                    ted.mPoints = new Point[1];
+                    ted.mPoints[0] = new Point(contentX, contentY);
                     ted.mMetaState = ev.getMetaState();
                     ted.mReprocess = mDeferTouchProcess;
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
@@ -5396,8 +5409,8 @@
                         if (inFullScreenMode() || mDeferTouchProcess) {
                             TouchEventData ted = new TouchEventData();
                             ted.mAction = WebViewCore.ACTION_DOUBLETAP;
-                            ted.mX = contentX;
-                            ted.mY = contentY;
+                            ted.mPoints = new Point[1];
+                            ted.mPoints[0] = new Point(contentX, contentY);
                             ted.mMetaState = ev.getMetaState();
                             ted.mReprocess = mDeferTouchProcess;
                             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
@@ -5513,14 +5526,32 @@
         return true;
     }
 
+    private void passMultiTouchToWebKit(MotionEvent ev) {
+        TouchEventData ted = new TouchEventData();
+        ted.mAction = ev.getAction() & MotionEvent.ACTION_MASK;
+        final int count = ev.getPointerCount();
+        ted.mPoints = new Point[count];
+        for (int c = 0; c < count; c++) {
+            int x = viewToContentX((int) ev.getX(c) + mScrollX);
+            int y = viewToContentY((int) ev.getY(c) + mScrollY);
+            ted.mPoints[c] = new Point(x, y);
+        }
+        ted.mMetaState = ev.getMetaState();
+        ted.mReprocess = mDeferTouchProcess;
+        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+        cancelLongPress();
+        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+        mPreventDefault = PREVENT_DEFAULT_IGNORE;
+    }
+
     private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) {
         if (shouldForwardTouchEvent()) {
             if (removeEvents) {
                 mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
             }
             TouchEventData ted = new TouchEventData();
-            ted.mX = x;
-            ted.mY = y;
+            ted.mPoints = new Point[1];
+            ted.mPoints[0] = new Point(x, y);
             ted.mAction = MotionEvent.ACTION_CANCEL;
             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
             mPreventDefault = PREVENT_DEFAULT_IGNORE;
@@ -6517,8 +6548,9 @@
                     if (inFullScreenMode() || mDeferTouchProcess) {
                         TouchEventData ted = new TouchEventData();
                         ted.mAction = WebViewCore.ACTION_LONGPRESS;
-                        ted.mX = viewToContentX((int) mLastTouchX + mScrollX);
-                        ted.mY = viewToContentY((int) mLastTouchY + mScrollY);
+                        ted.mPoints = new Point[1];
+                        ted.mPoints[0] = new Point(viewToContentX((int) mLastTouchX + mScrollX),
+                                                   viewToContentY((int) mLastTouchY + mScrollY));
                         // metaState for long press is tricky. Should it be the
                         // state when the press started or when the press was
                         // released? Or some intermediary key state? For
@@ -6655,6 +6687,12 @@
                     updateTextSelectionFromMessage(msg.arg1, msg.arg2,
                             (WebViewCore.TextSelectionData) msg.obj);
                     break;
+                case FORM_DID_BLUR:
+                    if (inEditingMode()
+                            && mWebTextView.isSameTextField(msg.arg1)) {
+                        hideSoftKeyboard();
+                    }
+                    break;
                 case RETURN_LABEL:
                     if (inEditingMode()
                             && mWebTextView.isSameTextField(msg.arg1)) {
@@ -6746,16 +6784,16 @@
                         TouchEventData ted = (TouchEventData) msg.obj;
                         switch (ted.mAction) {
                             case MotionEvent.ACTION_DOWN:
-                                mLastDeferTouchX = contentToViewX(ted.mX)
+                                mLastDeferTouchX = contentToViewX(ted.mPoints[0].x)
                                         - mScrollX;
-                                mLastDeferTouchY = contentToViewY(ted.mY)
+                                mLastDeferTouchY = contentToViewY(ted.mPoints[0].y)
                                         - mScrollY;
                                 mDeferTouchMode = TOUCH_INIT_MODE;
                                 break;
                             case MotionEvent.ACTION_MOVE: {
                                 // no snapping in defer process
-                                int x = contentToViewX(ted.mX) - mScrollX;
-                                int y = contentToViewY(ted.mY) - mScrollY;
+                                int x = contentToViewX(ted.mPoints[0].x) - mScrollX;
+                                int y = contentToViewY(ted.mPoints[0].y) - mScrollY;
                                 if (mDeferTouchMode != TOUCH_DRAG_MODE) {
                                     mDeferTouchMode = TOUCH_DRAG_MODE;
                                     mLastDeferTouchX = x;
@@ -6784,8 +6822,8 @@
                                 break;
                             case WebViewCore.ACTION_DOUBLETAP:
                                 // doDoubleTap() needs mLastTouchX/Y as anchor
-                                mLastTouchX = contentToViewX(ted.mX) - mScrollX;
-                                mLastTouchY = contentToViewY(ted.mY) - mScrollY;
+                                mLastTouchX = contentToViewX(ted.mPoints[0].x) - mScrollX;
+                                mLastTouchY = contentToViewY(ted.mPoints[0].y) - mScrollY;
                                 mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
                                 mDeferTouchMode = TOUCH_DONE_MODE;
                                 break;
@@ -7494,6 +7532,17 @@
     }
 
     /**
+     * Toggle whether multi touch events should be sent to webkit
+     * no matter if UI wants to handle it first.
+     *
+     * @hide This is only used by the webkit layout test.
+     */
+    public void setDeferMultiTouch(boolean value) {
+        mDeferMultitouch = value;
+        Log.v(LOGTAG, "set mDeferMultitouch to " + value);
+    }
+
+    /**
      *  Update our cache with updatedText.
      *  @param updatedText  The new text to put in our cache.
      */
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 122cf6a..7462668 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -273,6 +273,16 @@
         mCallbackProxy.onJsAlert(url, message);
     }
 
+    /**
+     * Called by JNI.  Send a message to the UI thread to hide the soft keyboard
+     * if the node pointed to by nodePointer is still in focus.
+     * @param nodePointer The node which just blurred.
+     */
+    private void formDidBlur(int nodePointer) {
+        if (mWebView == null) return;
+        Message.obtain(mWebView.mPrivateHandler, WebView.FORM_DID_BLUR,
+                nodePointer, 0).sendToTarget();
+    }
 
     /**
      * Called by JNI.  Open a file chooser to upload a file.
@@ -506,8 +516,8 @@
     private native void nativeTouchUp(int touchGeneration,
             int framePtr, int nodePtr, int x, int y);
 
-    private native boolean nativeHandleTouchEvent(int action, int x, int y,
-            int metaState);
+    private native boolean nativeHandleTouchEvent(int action, int[] x, int[] y,
+            int count, int metaState);
 
     private native void nativeUpdateFrameCache();
 
@@ -711,8 +721,7 @@
 
     static class TouchEventData {
         int mAction;
-        int mX;
-        int mY;
+        Point[] mPoints;
         int mMetaState;
         boolean mReprocess;
     }
@@ -1180,12 +1189,19 @@
 
                         case TOUCH_EVENT: {
                             TouchEventData ted = (TouchEventData) msg.obj;
+                            final int count = ted.mPoints.length;
+                            int[] xArray = new int[count];
+                            int[] yArray = new int[count];
+                            for (int c = 0; c < count; c++) {
+                                xArray[c] = ted.mPoints[c].x;
+                                yArray[c] = ted.mPoints[c].y;
+                            }
                             Message.obtain(
                                     mWebView.mPrivateHandler,
                                     WebView.PREVENT_TOUCH_ID,
                                     ted.mAction,
-                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
-                                            ted.mY, ted.mMetaState) ? 1 : 0,
+                                    nativeHandleTouchEvent(ted.mAction, xArray,
+                                            yArray, count, ted.mMetaState) ? 1 : 0,
                                     ted.mReprocess ? ted : null).sendToTarget();
                             break;
                         }
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index d7b4452..8f89678 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -67,6 +67,9 @@
     private final Object mFormLock = new Object();
     private final Object mHttpAuthLock = new Object();
 
+    // TODO: The Chromium HTTP stack handles cookies independently.
+    // We should consider removing the cookies table if and when we switch to
+    // the Chromium HTTP stack for good.
     private static final String mTableNames[] = {
         "cookies", "password", "formurl", "formdata", "httpauth"
     };
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 71f4f03..1bc0612 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -87,6 +87,7 @@
     private boolean mTouchable = true;
     private boolean mOutsideTouchable = false;
     private boolean mClippingEnabled = true;
+    private boolean mSplitTouchEnabled;
 
     private OnTouchListener mTouchInterceptor;
     
@@ -576,6 +577,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
@@ -900,6 +931,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/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index afb56fc..27f5ad4 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -122,7 +122,7 @@
 
                         // Post a runnable to call back to the view to notify it that we have
                         // connected
-                        adapter. mMainQueue.post(new Runnable() {
+                        adapter.mMainQueue.post(new Runnable() {
                             @Override
                             public void run() {
                                 final RemoteAdapterConnectionCallback callback =
@@ -148,7 +148,7 @@
             adapter.mMainQueue.removeMessages(0);
             adapter.mWorkerQueue.removeMessages(0);
 
-            // Clear the cache
+            // Clear the cache (the meta data will be re-requested on service re-connection)
             synchronized (adapter.mCache) {
                 adapter.mCache.reset();
             }
@@ -183,9 +183,13 @@
          *             successfully.
          */
         public void onRemoteViewsLoaded(RemoteViews view) {
-            // Remove all the children of this layout first
-            removeAllViews();
-            addView(view.apply(getContext(), this));
+            try {
+                // Remove all the children of this layout first
+                removeAllViews();
+                addView(view.apply(getContext(), this));
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to apply RemoteViews.");
+            }
         }
     }
 
@@ -224,6 +228,8 @@
          * the associated RemoteViews has loaded.
          */
         public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) {
+            if (view == null) return;
+
             final Integer pos = position;
             if (mReferences.containsKey(pos)) {
                 // Notify all the references for that position of the newly loaded RemoteViews
@@ -555,11 +561,14 @@
         }
 
         public void reset() {
+            // Note: We do not try and reset the meta data, since that information is still used by
+            // collection views to validate it's own contents (and will be re-requested if the data
+            // is invalidated through the notifyDataSetChanged() flow).
+
             mPreloadLowerBound = 0;
             mPreloadUpperBound = -1;
             mIndexRemoteViews.clear();
             mIndexMetaData.clear();
-            mMetaData.reset();
             synchronized (mLoadIndices) {
                 mRequestedIndices.clear();
                 mLoadIndices.clear();
@@ -834,11 +843,6 @@
     }
 
     public void notifyDataSetChanged() {
-        synchronized (mCache) {
-            // Flush the cache so that we can reload new items from the service
-            mCache.reset();
-        }
-
         final RemoteViewsMetaData metaData = mCache.getMetaData();
         synchronized (metaData) {
             // Set flag to calls the remote factory's onDataSetChanged() on the next worker loop
@@ -864,6 +868,11 @@
             }
         }
 
+        // Flush the cache so that we can reload new items from the service
+        synchronized (mCache) {
+            mCache.reset();
+        }
+
         // Re-request the new metadata (only after the notification to the factory)
         updateMetaData();
 
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index b9ded190..16126aa 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -82,27 +82,27 @@
         public RemoteViewsFactoryAdapter(RemoteViewsFactory factory) {
             mFactory = factory;
         }
-        public void onDataSetChanged() {
+        public synchronized void onDataSetChanged() {
             mFactory.onDataSetChanged();
         }
-        public int getCount() {
+        public synchronized int getCount() {
             return mFactory.getCount();
         }
-        public RemoteViews getViewAt(int position) {
+        public synchronized RemoteViews getViewAt(int position) {
             RemoteViews rv = mFactory.getViewAt(position);
             rv.setIsWidgetCollectionChild(true);
             return rv;
         }
-        public RemoteViews getLoadingView() {
+        public synchronized RemoteViews getLoadingView() {
             return mFactory.getLoadingView();
         }
-        public int getViewTypeCount() {
+        public synchronized int getViewTypeCount() {
             return mFactory.getViewTypeCount();
         }
-        public long getItemId(int position) {
+        public synchronized long getItemId(int position) {
             return mFactory.getItemId(position);
         }
-        public boolean hasStableIds() {
+        public synchronized boolean hasStableIds() {
             return mFactory.hasStableIds();
         }
 
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 09217af..dd67197 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.database.Cursor;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.text.Editable;
@@ -348,15 +349,19 @@
     }
 
     private void setImeVisibility(boolean visible) {
-        // We made sure the IME was displayed, so also make sure it is closed
-        // when we go away.
-        InputMethodManager imm = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-        if (imm != null) {
-            if (visible) {
-                imm.showSoftInputUnchecked(0, null);
-            } else {
-                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+        // don't mess with the soft input if we're not iconified by default
+        if (mIconifiedByDefault) {
+            InputMethodManager imm = (InputMethodManager)
+            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+            // We made sure the IME was displayed, so also make sure it is closed
+            // when we go away.
+            if (imm != null) {
+                if (visible) {
+                    imm.showSoftInputUnchecked(0, null);
+                } else {
+                    imm.hideSoftInputFromWindow(getWindowToken(), 0);
+                }
             }
         }
     }
@@ -717,4 +722,13 @@
         public void afterTextChanged(Editable s) {
         }
     };
-}
+
+    /*
+     * Avoid getting focus when searching for something to focus on.
+     * The user will have to touch the text view to get focus.
+     */
+    protected boolean onRequestFocusInDescendants(int direction,
+            Rect previouslyFocusedRect) {
+        return false;
+    }
+ }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d49b4d7f..f4d193f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7688,16 +7688,10 @@
 
     /**
      * 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()}.
@@ -7718,23 +7712,11 @@
         /**
          * Update the controller's position.
          */
-        public void updatePosition(int x, int y);
+        public void updatePosition(HandleView handle, int x, int y);
 
         public void updatePosition();
 
         /**
-         * 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();
-
-        /**
          * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
          * a chance to become active and/or visible.
          * @param event The touch event
@@ -7757,6 +7739,7 @@
             mDrawable = handle;
             mContainer = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
+            mContainer.setSplitTouchEnabled(true);
         }
 
         @Override
@@ -7855,7 +7838,7 @@
                 TextView.this.getLocationOnScreen(coords);
                 final int x = (int) (rawX - coords[0] + 0.5f);
                 final int y = (int) (rawY - coords[1] + 0.5f);
-                mController.updatePosition(x, y);
+                mController.updatePosition(this, x, y);
                 break;
 
             case MotionEvent.ACTION_UP:
@@ -7889,13 +7872,11 @@
         }
     }
 
-    class InsertionPointCursorController implements CursorController {
+    private class InsertionPointCursorController implements CursorController {
         private static final int DELAY_BEFORE_FADE_OUT = 4100;
 
         // The cursor controller image
         private final HandleView mHandle;
-        // Offset between finger hot point on cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
 
         private final Runnable mHider = new Runnable() {
             public void run() {
@@ -7928,7 +7909,7 @@
             return mHandle.isShowing();
         }
 
-        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);
 
@@ -7952,24 +7933,14 @@
             mHandle.positionAtCursor(offset, true);
         }
 
-        public float getOffsetX() {
-            return mOffsetX;
-        }
-
-        public float getOffsetY() {
-            return mOffsetY;
-        }
-
         public boolean onTouchEvent(MotionEvent ev) {
             return false;
         }
     }
 
-    class SelectionModifierCursorController implements CursorController {
+    private class SelectionModifierCursorController implements CursorController {
         // The cursor controller images
         private HandleView mStartHandle, mEndHandle;
-        // Offset between finger hot point on active cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
         // The offsets of that last touch down event. Remembered to start selection there.
         private int mMinTouchOffset, mMaxTouchOffset;
         // Whether selection anchors are active
@@ -8003,15 +7974,15 @@
             hide();
         }
 
-        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 = mStartHandle.isDragging() ? 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 (mStartHandle.isDragging()) {
+            if (handle == mStartHandle) {
                 if (offset <= selectionEnd) {
                     if (selectionStart == offset) {
                         return; // no change, no need to redraw;
@@ -8108,14 +8079,6 @@
             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.
          */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 66149ac..8ea02aa 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.util.JournaledFile;
 
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.net.TrafficStats;
 import android.os.BatteryManager;
@@ -50,6 +51,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -1150,7 +1152,11 @@
 
     private int getCurrentBluetoothPingCount() {
         if (mBtHeadset != null) {
-            return mBtHeadset.getBatteryUsageHint();
+            Set<BluetoothDevice> deviceSet = mBtHeadset.getConnectedDevices();
+            BluetoothDevice[] devices = deviceSet.toArray(new BluetoothDevice[deviceSet.size()]);
+            if (devices.length > 0) {
+                return mBtHeadset.getBatteryUsageHint(devices[0]);
+            }
         }
         return -1;
     }
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
index aa340fb..2b96bf6 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -121,6 +121,10 @@
         return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
     }
 
+    public boolean isClearable() {
+        return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0)
+                && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
+    }
 }
 
 
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 8311c80..d517d4c 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -84,7 +84,7 @@
     static final int THEME_RES_FOR_TYPE[] = new int[] {
         com.android.internal.R.style.Theme_IconMenu,
         com.android.internal.R.style.Theme_ExpandedMenu,
-        0,
+        com.android.internal.R.style.Theme_Light,
         -1,
         -1,
     };
diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl
new file mode 100644
index 0000000..35746ad
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import com.trustedlogic.trustednfc.android.LlcpPacket;
+
+/**
+ * TODO
+ *
+ * {@hide}
+ */
+interface ILlcpConnectionlessSocket
+{
+
+    void        close(int nativeHandle);
+    int         getSap(int nativeHandle);
+    LlcpPacket  receiveFrom(int nativeHandle);
+    int        sendTo(int nativeHandle, in LlcpPacket packet);
+    
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl
new file mode 100644
index 0000000..5eb1f3c
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+/**
+ * TODO
+ *
+ * {@hide}
+ */
+interface ILlcpServiceSocket
+{
+
+    int     accept(int nativeHandle);
+    void    close(int nativeHandle);
+    int     getAcceptTimeout(int nativeHandle);
+    void    setAcceptTimeout(int nativeHandle, int timeout);
+    
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl
new file mode 100644
index 0000000..e9169d8
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+/**
+ * TODO
+ *
+ * {@hide}
+ */
+interface ILlcpSocket
+{
+ 
+    int     close(int nativeHandle);
+    int     connect(int nativeHandle, int sap);
+    int     connectByName(int nativeHandle, String sn);
+    int     getConnectTimeout(int nativeHandle);
+    int     getLocalSap(int nativeHandle);
+    int     getLocalSocketMiu(int nativeHandle);
+    int     getLocalSocketRw(int nativeHandle);
+    int     getRemoteSocketMiu(int nativeHandle);
+    int     getRemoteSocketRw(int nativeHandle);
+    int     receive(int nativeHandle, out byte[] receiveBuffer);
+    int     send(int nativeHandle, in byte[] data);
+    void    setConnectTimeout(int nativeHandle, int timeout);
+    
+}
+
diff --git a/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl b/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl
new file mode 100644
index 0000000..1f8d1a4
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import com.trustedlogic.trustednfc.android.NdefMessage;
+
+/**
+ * TODO
+ *
+ * {@hide}
+ */
+interface INdefTag
+{
+
+    NdefMessage read(int nativeHandle);
+    boolean     write(int nativeHandle, in NdefMessage msg);
+
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl b/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl
new file mode 100644
index 0000000..ce36ab2
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import com.trustedlogic.trustednfc.android.ILlcpSocket;
+import com.trustedlogic.trustednfc.android.ILlcpServiceSocket;
+import com.trustedlogic.trustednfc.android.ILlcpConnectionlessSocket;
+import com.trustedlogic.trustednfc.android.INfcTag;
+import com.trustedlogic.trustednfc.android.IP2pTarget;
+import com.trustedlogic.trustednfc.android.IP2pInitiator;
+
+
+/**
+ * Interface that allows controlling NFC activity.
+ *
+ * {@hide}
+ */
+interface INfcManager
+{
+
+    ILlcpSocket                 getLlcpInterface();
+    ILlcpConnectionlessSocket   getLlcpConnectionlessInterface();
+    ILlcpServiceSocket          getLlcpServiceInterface();
+    INfcTag                     getNfcTagInterface();
+    IP2pTarget                  getP2pTargetInterface();
+    IP2pInitiator               getP2pInitiatorInterface();
+    
+    void    cancel();
+    int     createLlcpConnectionlessSocket(int sap);
+    int     createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength);
+    int     createLlcpSocket(int sap, int miu, int rw, int linearBufferLength);
+    int     deselectSecureElement();
+    boolean disable();
+    boolean enable();
+    int     getOpenTimeout();
+    String  getProperties(String param);
+    int[]   getSecureElementList();
+    int     getSelectedSecureElement();
+    boolean isEnabled();
+    int     openP2pConnection();
+    int     openTagConnection();
+    int     selectSecureElement(int seId);
+    void    setOpenTimeout(int timeout);
+    int     setProperties(String param, String value);
+
+}
+
diff --git a/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl b/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl
new file mode 100644
index 0000000..79543c4
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import com.trustedlogic.trustednfc.android.NdefMessage;
+
+/**
+ * TODO
+ *
+ * {@hide}
+ */
+interface INfcTag
+{
+
+    int     close(int nativeHandle);
+    int     connect(int nativeHandle);
+    String  getType(int nativeHandle);
+    byte[]  getUid(int nativeHandle);
+    boolean isNdef(int nativeHandle);
+    byte[]  transceive(int nativeHandle, in byte[] data);
+    
+    NdefMessage read(int nativeHandle);
+    boolean     write(int nativeHandle, in NdefMessage msg);
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl b/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
new file mode 100644
index 0000000..96819ae
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+/**
+ * TODO
+ *
+ * {@hide}
+ */
+interface IP2pInitiator
+{
+
+    byte[]   getGeneralBytes(int nativeHandle);
+    int      getMode(int nativeHandle);
+    byte[]   receive(int nativeHandle);
+    boolean  send(int nativeHandle, in byte[] data);
+
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl b/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl
new file mode 100644
index 0000000..8dcdf18
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+/**
+ * TODO
+ *
+ * {@hide}
+ */
+interface IP2pTarget
+{
+
+    byte[]     getGeneralBytes(int nativeHandle);
+    int        getMode(int nativeHandle);
+    int    	   connect(int nativeHandle);
+    boolean    disconnect(int nativeHandle);
+    byte[]     transceive(int nativeHandle, in byte[] data);
+ 
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java
new file mode 100644
index 0000000..0270626
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : LlcpConnectionLessSocket.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * LlcpConnectionlessSocket represents a LLCP Connectionless object to be used
+ * in a connectionless communication
+ * 
+ * @since AA02.01
+ * @hide
+ */
+public class LlcpConnectionlessSocket {
+	
+	
+    private static final String TAG = "LlcpConnectionlessSocket";
+
+    /**
+     * The handle returned by the NFC service and used to identify the LLCP connectionless socket in
+     * every call of this class.
+     * 
+     * @hide
+     */
+    protected int mHandle;
+
+
+    /**
+     * The entry point for LLCP Connectionless socket operations.
+     * 
+     * @hide
+     */
+    protected ILlcpConnectionlessSocket mService;
+	
+	
+    /**
+     * Internal constructor for the LlcpConnectionlessSocket class.
+     * 
+     * @param service The entry point to the Nfc Service for  LLCP Connectionless socket  class.
+     * @param handle The handle returned by the NFC service and used to identify
+     *            the socket in subsequent calls.
+     * @hide
+     */
+	LlcpConnectionlessSocket(ILlcpConnectionlessSocket service, int handle) {
+        this.mService = service;
+        this.mHandle = handle;
+    }
+
+    /**
+     * Send data to a specific LLCP Connectionless client
+     * 
+     * @param packet Service Access Point number related to a LLCP
+     *            Connectionless client and a data buffer to send
+     * @throws IOException if the LLCP link has been lost or deactivated.
+     * @since AA02.01
+     */
+    public void sendTo(LlcpPacket packet) throws IOException {
+		try {
+			int result = mService.sendTo(mHandle, packet);
+			// Handle potential errors
+			if (ErrorCodes.isError(result)) {
+				throw new IOException();
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in sendTo(): ", e);
+		}
+    }
+
+    /**
+     * Receive data from a LLCP Connectionless client
+     * 
+     * @return data data received from a specific LLCP Connectionless client
+     * @throws IOException if the LLCP link has been lost or deactivated.
+     * @see LlcpPacket
+     * @since AA02.01
+     */
+    public LlcpPacket receiveFrom() throws IOException {
+		try {
+			LlcpPacket packet = mService.receiveFrom(mHandle);
+			if (packet != null) {
+				return packet;
+			}else{
+				// Handle potential errors
+				throw new IOException();			
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in receiveFrom(): ", e);
+		}
+        return null;
+    }
+
+    /**
+     * Close the created Connectionless socket.
+     * 
+     * @since AA02.01
+     */
+    public void close() {
+		try {
+			mService.close(mHandle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in close(): ", e);
+		}
+    }
+
+    /**
+     * Returns the local Service Access Point number of the socket
+     * 
+     * @return sap
+     * @since AA02.01
+     */
+    public int getSap() {
+    	int sap = 0;
+    	
+    	try {
+			sap = mService.getSap(mHandle);
+
+		} catch (RemoteException e) {
+
+			e.printStackTrace();
+		}
+    	return sap;
+    }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpException.java b/core/java/com/trustedlogic/trustednfc/android/LlcpException.java
new file mode 100644
index 0000000..1e2e2da
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpException.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : LLCPException.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ * Created         : 24-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+/**
+ * Generic exception thrown in case something unexpected happened during a 
+ * LLCP communication.
+ *
+ * @since AA02.01
+ * @hide
+ */
+public class LlcpException extends Exception {
+   /**
+    * Constructs a new LlcpException with the current stack trace and the
+    * specified detail message.
+    *
+    * @param s the detail message for this exception.
+    */
+   public LlcpException(String s) {
+      super(s);
+   }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl
new file mode 100644
index 0000000..297a1fe
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+parcelable LlcpPacket;
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java
new file mode 100644
index 0000000..af79023
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : LLCPPacket.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ * Created         : 25-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a LLCP packet received in a LLCP Connectionless communication;
+ * 
+ * @since AA02.01
+ * @hide
+ */
+public class LlcpPacket implements Parcelable {
+
+    private int mRemoteSap;
+
+    private byte[] mDataBuffer;
+
+    /**
+     * Creator class, needed when implementing from Parcelable
+     * {@hide}
+     */
+    public static final Parcelable.Creator<LlcpPacket> CREATOR = new Parcelable.Creator<LlcpPacket>() {
+        public LlcpPacket createFromParcel(Parcel in) {
+            // Remote SAP
+            short sap = (short)in.readInt();
+            
+            // Data Buffer
+            int dataLength = in.readInt();
+            byte[] data = new byte[dataLength];
+            in.readByteArray(data);
+            
+            return new LlcpPacket(sap, data);
+        }
+
+        public LlcpPacket[] newArray(int size) {
+            return new LlcpPacket[size];
+        }
+    };
+    
+
+    /**
+     * Creates a LlcpPacket to be sent to a remote Service Access Point number
+     * (SAP)
+     * 
+     * @param sap Remote Service Access Point number
+     * @param data Data buffer
+     * @since AA02.01
+     */
+    public LlcpPacket(int sap, byte[] data) {
+    	mRemoteSap = sap;
+        mDataBuffer = data;
+    }
+    
+    /**
+     * @hide
+     */
+    public LlcpPacket() {
+    }
+
+    /**
+     * Returns the remote Service Access Point number
+     * 
+     * @return remoteSap
+     * @since AA02.01
+     */
+    public int getRemoteSap() {
+        return mRemoteSap;
+    }
+
+    /**
+     * Returns the data buffer
+     * 
+     * @return data
+     * @since AA02.01
+     */
+    public byte[] getDataBuffer() {
+        return mDataBuffer;
+    }
+
+    /**
+     * (Parcelable) Describe the parcel
+     * {@hide}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * (Parcelable) Convert current object to a Parcel
+     * {@hide}
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRemoteSap);
+        dest.writeInt(mDataBuffer.length);
+        dest.writeByteArray(mDataBuffer);      
+    }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java
new file mode 100644
index 0000000..a152ecb1
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : LLCPServerSocket.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * LlcpServiceSocket represents a LLCP Service to be used in a
+ * Connection-oriented communication
+ * 
+ * @since AA02.01
+ * @hide
+ */
+public class LlcpServiceSocket {
+
+	private static final String TAG = "LlcpServiceSocket";
+
+	/**
+	 * The handle returned by the NFC service and used to identify the LLCP
+	 * Service socket in every call of this class.
+	 * 
+	 * @hide
+	 */
+	protected int mHandle;
+
+	/**
+	 * The entry point for LLCP Service socket operations.
+	 * 
+	 * @hide
+	 */
+	protected ILlcpServiceSocket mService;
+	
+    private ILlcpSocket mLlcpSocketService;
+
+	static LlcpException convertErrorToLlcpException(int errorCode) {
+		return convertErrorToLlcpException(errorCode, null);
+	}
+
+	static LlcpException convertErrorToLlcpException(int errorCode,
+			String message) {
+		if (message == null) {
+			message = "";
+		} else {
+			message = " (" + message + ")";
+		}
+
+		switch (errorCode) {
+		case ErrorCodes.ERROR_SOCKET_CREATION:
+			return new LlcpException(
+					"Error during the creation of an Llcp socket" + message);
+		case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+			return new LlcpException("Not enough ressources are available"
+					+ message);
+		default:
+			return new LlcpException("Unkown error code " + errorCode + message);
+		}
+	}
+
+	/**
+	 * Internal constructor for the LlcpServiceSocket class.
+	 * 
+	 * @param service
+	 *            The entry point to the Nfc Service for LlcpServiceSocket
+	 *            class.
+	 * @param handle
+	 *            The handle returned by the NFC service and used to identify
+	 *            the socket in subsequent calls.
+	 * @hide
+	 */
+	LlcpServiceSocket(ILlcpServiceSocket service, ILlcpSocket socketService, int handle) {
+		this.mService = service;
+		this.mHandle = handle;
+		this.mLlcpSocketService = socketService;
+	}
+
+	/**
+	 * Wait for incomming connection request from a LLCP client and accept this
+	 * request
+	 * 
+	 * @return socket object to be used to communicate with a LLCP client
+	 * 
+	 * @throws IOException
+	 *             if the llcp link is lost or deactivated
+	 * @throws LlcpException
+	 *             if not enough ressources are available
+	 * 
+	 * @see LlcpSocket
+	 * @since AA02.01
+	 */
+	public LlcpSocket accept() throws IOException, LlcpException {
+
+		try {
+			int handle = mService.accept(mHandle);
+			// Handle potential errors
+			if (ErrorCodes.isError(handle)) {
+				if (handle == ErrorCodes.ERROR_IO) {
+					throw new IOException();
+				} else {
+					throw convertErrorToLlcpException(handle);
+				}
+			}
+	        
+			// Build the public LlcpSocket object
+			return new LlcpSocket(mLlcpSocketService, handle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in accept(): ", e);
+			return null;
+		}
+
+	}
+
+	/**
+	 * Set the timeout for the accept request
+	 * 
+	 * @param timeout
+	 *            value of the timeout for the accept request
+	 * @since AA02.01
+	 */
+	public void setAcceptTimeout(int timeout) {
+		try {
+			mService.setAcceptTimeout(mHandle, timeout);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in setAcceptTimeout(): ", e);
+		}
+	}
+
+	/**
+	 * Get the timeout value of the accept request
+	 * 
+	 * @return mTimeout
+	 * @since AA02.01
+	 */
+	public int getAcceptTimeout() {
+		try {
+			return mService.getAcceptTimeout(mHandle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in getAcceptTimeout(): ", e);
+			return 0;
+		}
+	}
+
+	/**
+	 * Close the created Llcp Service socket
+	 * 
+	 * @since AA02.01
+	 */
+	public void close() {
+		try {
+			mService.close(mHandle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in close(): ", e);
+		}
+	}
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java
new file mode 100644
index 0000000..e47160c
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : LlcpClientSocket.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a
+ * connection-oriented communication
+ * 
+ * @since AA02.01
+ * @hide
+ */
+public class LlcpSocket {
+
+	private static final String TAG = "LlcpSocket";
+
+	/**
+	 * The handle returned by the NFC service and used to identify the LLCP
+	 * socket in every call of this class.
+	 * 
+	 * @hide
+	 */
+	protected int mHandle;
+
+	/**
+	 * The entry point for LLCP socket operations.
+	 * 
+	 * @hide
+	 */
+	protected ILlcpSocket mService;
+
+	static LlcpException convertErrorToLlcpException(int errorCode) {
+		return convertErrorToLlcpException(errorCode, null);
+	}
+
+	static LlcpException convertErrorToLlcpException(int errorCode,
+			String message) {
+		if (message == null) {
+			message = "";
+		} else {
+			message = " (" + message + ")";
+		}
+
+		switch (errorCode) {
+		case ErrorCodes.ERROR_SOCKET_CREATION:
+			return new LlcpException(
+					"Error during the creation of an Llcp socket" + message);
+		case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+			return new LlcpException("Not enough ressources are available"
+					+ message);
+		case ErrorCodes.ERROR_SOCKET_NOT_CONNECTED:
+			return new LlcpException("Socket not connected to an Llcp Service"
+					+ message);
+		default:
+			return new LlcpException("Unkown error code " + errorCode + message);
+		}
+	}
+
+	/**
+	 * Internal constructor for the LlcpSocket class.
+	 * 
+	 * @param service
+	 *            The entry point to the Nfc Service for LlcpServiceSocket
+	 *            class.
+	 * @param handle
+	 *            The handle returned by the NFC service and used to identify
+	 *            the socket in subsequent calls.
+	 * @hide
+	 */
+	LlcpSocket(ILlcpSocket service, int handle) {
+		this.mService = service;
+		this.mHandle = handle;
+	}
+
+	/**
+	 * Connect request to a specific LLCP Service by its SAP.
+	 * 
+	 * @param sap
+	 *            Service Access Point number of the LLCP Service
+	 * @throws IOException
+	 *             if the LLCP has been lost or deactivated.
+	 * @throws LlcpException
+	 *             if the connection request is rejected by the remote LLCP
+	 *             Service
+	 * @since AA02.01
+	 */
+	public void connect(int sap) throws IOException, LlcpException {
+		try {
+			int result = mService.connect(mHandle, sap);
+			// Handle potential errors
+			if (ErrorCodes.isError(result)) {
+				if (result == ErrorCodes.ERROR_IO) {
+					throw new IOException();
+				} else {
+					throw convertErrorToLlcpException(result);
+				}
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in accept(): ", e);
+		}
+	}
+
+	/**
+	 * Connect request to a specific LLCP Service by its Service Name.
+	 * 
+	 * @param sn
+	 *            Service Name of the LLCP Service
+	 * @throws IOException
+	 *             if the LLCP has been lost or deactivated.
+	 * @throws LlcpException
+	 *             if the connection request is rejected by the remote LLCP
+	 *             Service
+	 * @since AA02.01
+	 */
+	public void connect(String sn) throws IOException, LlcpException {
+		try {
+			int result = mService.connectByName(mHandle, sn);
+			// Handle potential errors
+			if (ErrorCodes.isError(result)) {
+				if (result == ErrorCodes.ERROR_IO) {
+					throw new IOException();
+				} else {
+					throw convertErrorToLlcpException(result);
+				}
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in accept(): ", e);
+		}
+	}
+
+	/**
+	 * Set the timeout for the connect request
+	 * 
+	 * @param timeout
+	 *            timeout value for the connect request
+	 * @since AA02.01
+	 */
+	public void setConnectTimeout(int timeout) {
+		try {
+			mService.setConnectTimeout(mHandle, timeout);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in setConnectTimeout(): ", e);
+		}
+	}
+
+	/**
+	 * Get the timeout value of the connect request
+	 * 
+	 * @return mTimeout
+	 * @since AA02.01
+	 */
+	public int getConnectTimeout() {
+		try {
+			return mService.getConnectTimeout(mHandle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in getConnectTimeout(): ", e);
+			return 0;
+		}
+	}
+
+	/**
+	 * Disconnect request to the connected LLCP socket and close the created
+	 * socket.
+	 * 
+	 * @throws IOException
+	 *             if the LLCP has been lost or deactivated.
+	 * @since AA02.01
+	 */
+	public void close() throws IOException {
+		try {
+			int result = mService.close(mHandle);
+			// Handle potential errors
+			if (ErrorCodes.isError(result)) {
+				throw new IOException();
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in close(): ", e);
+		}
+	}
+
+	/**
+	 * Send data to the connected LLCP Socket.
+	 * 
+	 * @throws IOException
+	 *             if the LLCP has been lost or deactivated.
+	 * @since AA02.01
+	 */
+	public void send(byte[] data) throws IOException {
+		try {
+			int result = mService.send(mHandle, data);
+			// Handle potential errors
+			if (ErrorCodes.isError(result)) {
+				throw new IOException();
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in send(): ", e);
+		}		
+	}
+
+	/**
+	 * Receive data from the connected LLCP socket
+	 * 
+	 * @param receiveBuffer
+	 *            a buffer for the received data
+	 * @return length length of the data received
+	 * @throws IOException
+	 *             if the LLCP has been lost or deactivated.
+	 * @since AA02.01
+	 */
+	public int receive(byte[] receiveBuffer) throws IOException {
+		int receivedLength = 0;
+		try {
+			receivedLength = mService.receive(mHandle, receiveBuffer);
+			if(receivedLength == 0){
+				throw new IOException();
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in send(): ", e);
+		}	
+		
+		return receivedLength;
+	}
+	
+	/**
+	 * Returns the local Service Access Point number of the socket
+	 * 
+	 * @return localSap
+	 * @since AA02.01
+	 */
+	public int getLocalSap() {
+		try {
+			return  mService.getLocalSap(mHandle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in getLocalSap(): ", e);
+			return 0;
+		}
+	}
+
+	/**
+	 * Returns the local Maximum Information Unit(MIU) of the socket
+	 * 
+	 * @return miu
+	 * @since AA02.01
+	 */
+	public int getLocalSocketMiu() {
+		try {
+			return  mService.getLocalSocketMiu(mHandle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in getLocalSocketMiu(): ", e);
+			return 0;
+		}
+	}
+
+	/**
+	 * Returns the local Receive Window(RW) of the socket
+	 * 
+	 * @return rw
+	 * @since AA02.01
+	 */
+	public int getLocalSocketRw() {
+		try {
+			return  mService.getLocalSocketRw(mHandle);
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in getLocalSocketRw(): ", e);
+			return 0;
+		}
+	}
+
+	/**
+	 * Returns the remote Maximum Information Unit(MIU) of the socket.
+	 * <p>
+	 * This method must be called when the socket is in CONNECTED_STATE
+	 * 
+	 * @return remoteMiu
+	 * @throws LlcpException
+	 *             if the LlcpClientSocket is not in a CONNECTED_STATE
+	 * @since AA02.01
+	 */
+	public int getRemoteSocketMiu() throws LlcpException {
+		try {
+			int result = mService.getRemoteSocketMiu(mHandle);
+			if(result != ErrorCodes.ERROR_SOCKET_NOT_CONNECTED){
+				return result;
+			}else{
+				throw convertErrorToLlcpException(result);
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in getRemoteSocketMiu(): ", e);
+			return 0;
+		}
+	}
+
+	/**
+	 * Returns the remote Receive Window(RW) of the connected remote socket.
+	 * <p>
+	 * This method must be called when the socket is in CONNECTED_STATE
+	 * 
+	 * @return rw
+	 * @throws LlcpException
+	 *             if the LlcpClientSocket is not in a CONNECTED_STATE
+	 * @since AA02.01
+	 */
+	public int getRemoteSocketRw() throws LlcpException {
+		try {
+			int result = mService.getRemoteSocketRw(mHandle);
+			if( result != ErrorCodes.ERROR_SOCKET_NOT_CONNECTED){
+				return result;
+			}else{
+				throw convertErrorToLlcpException(result);
+			}
+		} catch (RemoteException e) {
+			Log.e(TAG, "RemoteException in getRemoteSocketRw(): ", e);
+			return 0;
+		}
+	}
+
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
new file mode 100644
index 0000000..e60f4e8
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.trustedlogic.trustednfc.android;
+
+parcelable NdefMessage;
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java
new file mode 100644
index 0000000..f03b604
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NDEFMessage.java
+ * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
+ * Created         : 05-10-2009
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents an NDEF message as specified by the <a
+ * href="http://www.nfc-forum.org/">NFC Forum</a>.
+ * 
+ * @see NdefRecord
+ * 
+ * @since AA01.04
+ * @hide
+ */
+public class NdefMessage implements Parcelable {
+	/* Flag values */
+	private static final int FLAG_MB = 0x80;
+	private static final int FLAG_ME = 0x40;
+	private static final int FLAG_CF = 0x20;
+	private static final int FLAG_SR = 0x10;
+	private static final int FLAG_IL = 0x08;
+
+	/**
+	 * Array of {@link NdefRecord} composing this message.
+	 */
+	private NdefRecord[] mRecords;
+
+	/**
+	 * Builds an NDEF message.
+	 * 
+	 * @param data raw NDEF message data
+	 * 
+	 * @throws NFCException
+	 */
+	public NdefMessage(byte[] data) throws NfcException {
+		if (parseNdefMessage(data) == -1)
+			throw new NfcException("Error while parsing NDEF message");
+	}
+
+	/**
+	 * Builds an NDEF message.
+	 * 
+	 * @param records
+	 *            an array of already created NDEF records
+	 */
+	public NdefMessage(NdefRecord[] records) {
+		mRecords = new NdefRecord[records.length];
+
+		System.arraycopy(records, 0, mRecords, 0, records.length);
+	}
+
+	/**
+	 * Returns the NDEF message as a byte array.
+	 * 
+	 * @return the message as a byte array
+	 */
+	public byte[] toByteArray() {
+		if ((mRecords == null) || (mRecords.length == 0))
+			return null;
+
+		byte[] msg = {};
+
+		for (int i = 0; i < mRecords.length; i++) {
+			byte[] record = mRecords[i].toByteArray();
+			byte[] tmp = new byte[msg.length + record.length];
+
+			/* Make sure the Message Begin flag is set only for the first record */
+			if (i == 0)
+				record[0] |= FLAG_MB;
+			else
+				record[0] &= ~FLAG_MB;
+
+			/* Make sure the Message End flag is set only for the last record */
+			if (i == (mRecords.length - 1))
+				record[0] |= FLAG_ME;
+			else
+				record[0] &= ~FLAG_ME;
+
+			System.arraycopy(msg, 0, tmp, 0, msg.length);
+			System.arraycopy(record, 0, tmp, msg.length, record.length);
+
+			msg = tmp;
+		}
+
+		return msg;
+	}
+	
+	/**
+	* Returns an array of {@link NdefRecord} composing this message.
+	*
+	* @return mRecords
+	* 
+	* @since AA02.01
+	*/
+	public NdefRecord[] getRecords(){
+		return mRecords;
+	}
+	
+	private native int parseNdefMessage(byte[] data);
+
+    /**
+     * (Parcelable) Describe the parcel
+     * {@hide}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * (Parcelable) Convert current object to a Parcel
+     * {@hide}
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRecords.length);
+        dest.writeTypedArray(mRecords, 0);
+    }
+
+    /**
+     * Creator class, needed when implementing from Parcelable
+     * {@hide}
+     */
+    public static final Parcelable.Creator<NdefMessage> CREATOR = new Parcelable.Creator<NdefMessage>() {
+        public NdefMessage createFromParcel(Parcel in) {
+            int recordsLength = in.readInt();
+            NdefRecord[] records = new NdefRecord[recordsLength];
+            in.readTypedArray(records, NdefRecord.CREATOR);
+            return new NdefMessage(records);
+        }
+
+        public NdefMessage[] newArray(int size) {
+            return new NdefMessage[size];
+        }
+    };
+    
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl
new file mode 100644
index 0000000..9d95174
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+parcelable NdefRecord;
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java
new file mode 100644
index 0000000..a0257fe
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NdefRecord.java
+ * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
+ * Created         : 05-10-2009
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import android.location.Location;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An NDEF record as specified by the <a href="http://www.nfc-forum.org/">NFC
+ * Forum</a>.
+ * 
+ * @see NdefMessage
+ * 
+ * @since AA01.04
+ * @hide
+ */
+public class NdefRecord implements Parcelable {
+
+	/**
+	 * Type Name Format - Empty record
+	 */
+	public static final short TNF_EMPTY = 0x0;
+
+	/**
+	 * Type Name Format - NFC Forum-defined type
+	 */
+	public static final short TNF_WELL_KNOWN_TYPE = 0x1;
+
+	/**
+	 * Type Name Format - RFC2045 MIME type
+	 */
+	public static final short TNF_MIME_MEDIA_TYPE = 0x2;
+
+	/**
+	 * Type Name Format - Absolute URI
+	 */
+	public static final short TNF_ABSOLUTE_URI = 0x3;
+
+	/**
+	 * Type Name Format - User-defined type
+	 */
+	public static final short TNF_EXTERNAL_TYPE = 0x4;
+
+	/**
+	 * Type Name Format - Unknown type
+	 */
+	public static final short TNF_UNKNOWN = 0x5;
+
+	/**
+	 * Type Name Format - Unchanged. This TNF is used for chunked records, so
+	 * that middle records inherits from the first record's type.
+	 */
+	public static final short TNF_UNCHANGED = 0x6;
+
+	/**
+	 * NFC Forum-defined Type - Smart Poster
+	 */
+	public static final byte[] TYPE_SMART_POSTER = { 0x53, 0x70 };
+
+	/**
+	 * NFC Forum-defined Type - Text
+	 */
+	public static final byte[] TYPE_TEXT = { 0x54 };
+
+	/**
+	 * NFC Forum-defined Type - URI
+	 */
+	public static final byte[] TYPE_URI = { 0x55 };
+
+	/**
+	 * NFC Forum-defined Global Type - Connection Handover Request
+	 */
+	public static final byte[] TYPE_HANDOVER_REQUEST = { 0x48, 0x72 };
+
+	/**
+	 * NFC Forum-defined Global Type - Connection Handover Select
+	 */
+	public static final byte[] TYPE_HANDOVER_SELECT = { 0x48, 0x73 };
+
+	/**
+	 * NFC Forum-defined Global Type - Connection Handover Carrier
+	 */
+	public static final byte[] TYPE_HANDOVER_CARRIER = { 0x48, 0x63 };
+
+	/**
+	 * NFC Forum-defined Local Type - Alternative Carrier
+	 */
+	public static final byte[] TYPE_ALTERNATIVE_CARRIER = { 0x61, 0x63 };
+
+	/* Flag values */
+	private static final int FLAG_MB = 0x80;
+	private static final int FLAG_ME = 0x40;
+	private static final int FLAG_CF = 0x20;
+	private static final int FLAG_SR = 0x10;
+	private static final int FLAG_IL = 0x08;
+
+	/**
+	 * Record Flags
+	 */
+	private short mFlags = 0;
+
+	/**
+	 * Record Type Name Format
+	 */
+	private short mTnf = 0;
+
+	/**
+	 * Record Type
+	 */
+	private byte[] mType = null;
+
+	/**
+	 * Record Identifier
+	 */
+	private byte[] mId = null;
+
+	/**
+	 * Record Payload
+	 */
+	private byte[] mPayload = null;
+
+	/**
+	 * Creates an NdefRecord given its Type Name Format, its type, its id and
+	 * its.
+	 * 
+	 * @param tnf
+	 *            Type Name Format
+	 * @param type
+	 *            record type
+	 * @param id
+	 *            record id (optional, can be null)
+	 * @param data
+	 *            record payload
+	 */
+	public NdefRecord(short tnf, byte[] type, byte[] id, byte[] data) {
+		
+		/* generate flag */
+		mFlags = FLAG_MB | FLAG_ME;
+		
+		/* Determine if it is a short record */
+		if(data.length < 0xFF)
+		{
+			mFlags |= FLAG_SR;
+		}
+		
+		/* Determine if an id is present */
+		if(id.length != 0)
+		{
+			mFlags |= FLAG_IL;
+		}
+		
+		mTnf = tnf;
+		mType = (byte[]) type.clone();
+		mId = (byte[]) id.clone();
+		mPayload = (byte[]) data.clone();
+	}
+
+	/**
+	 * Appends data to the record's payload.
+	 * 
+	 * @param data
+	 *            Data to be added to the record.
+	 */
+	public void appendPayload(byte[] data) {
+		byte[] newPayload = new byte[mPayload.length + data.length];
+
+		System.arraycopy(mPayload, 0, newPayload, 0, mPayload.length);
+		System.arraycopy(data, 0, newPayload, mPayload.length, data.length);
+
+		mPayload = newPayload;
+	}
+
+	/**
+	 * Returns record as a byte array.
+	 * 
+	 * @return record as a byte array.
+	 */
+	public byte[] toByteArray() {
+		return generate(mFlags, mTnf, mType, mId, mPayload);
+	}
+
+	private native byte[] generate(short flags, short tnf, byte[] type,
+			byte[] id, byte[] data);
+
+    /**
+     * (Parcelable) Describe the parcel
+     * {@hide}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * (Parcelable) Convert current object to a Parcel
+     * {@hide}
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mTnf);
+        dest.writeInt(mType.length);
+        dest.writeByteArray(mType);
+        dest.writeInt(mId.length);
+        dest.writeByteArray(mId);
+        dest.writeInt(mPayload.length);
+        dest.writeByteArray(mPayload);
+    }
+
+    /**
+     * Creator class, needed when implementing from Parcelable
+     * {@hide}
+     */
+    public static final Parcelable.Creator<NdefRecord> CREATOR = new Parcelable.Creator<NdefRecord>() {
+        public NdefRecord createFromParcel(Parcel in) {
+            // TNF
+            short tnf = (short)in.readInt();
+            // Type
+            int typeLength = in.readInt();
+            byte[] type = new byte[typeLength];
+            in.readByteArray(type);
+            // ID
+            int idLength = in.readInt();
+            byte[] id = new byte[idLength];
+            in.readByteArray(id);
+            // Payload
+            int payloadLength = in.readInt();
+            byte[] payload = new byte[payloadLength];
+            in.readByteArray(payload);
+            
+            return new NdefRecord(tnf, type, id, payload);
+        }
+
+        public NdefRecord[] newArray(int size) {
+            return new NdefRecord[size];
+        }
+    };
+    
+    /**
+     * Returns record TNF
+     * 
+     * @return mTnf
+     */
+    public int getTnf(){
+    	return mTnf;
+    }
+    
+    /**
+     * Returns record TYPE
+     * 
+     * @return mType
+     */
+    public byte[] getType(){
+    	return mType;
+    }
+   
+    /**
+     * Returns record ID
+     * 
+     * @return mId
+     */
+    public byte[] getId(){
+    	return mId;
+    }
+    
+    /**
+     * Returns record Payload
+     * 
+     * @return mPayload
+     */
+    public byte[] getPayload(){
+    	return mPayload;
+    }
+    
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefTag.java b/core/java/com/trustedlogic/trustednfc/android/NdefTag.java
new file mode 100644
index 0000000..1d99241
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NdefTag.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NDEFTag.java
+ * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
+ * Created         : 04-12-2009
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * NdefTag represents tags complying with the NFC Forum's NFC Data Exchange
+ * Format.
+ * 
+ * @since AA01.04
+ * @hide
+ */
+public class NdefTag extends NfcTag {
+
+    private static final String TAG = "NdefTag";
+
+
+    public NdefTag(NfcTag tag){
+		super(tag.mService,tag.mHandle);
+		this.isConnected = tag.isConnected; 
+		this.isClosed = tag.isClosed;
+		tag.isClosed = false;
+    }
+    
+    /**
+     * Internal constructor for the NfcNdefTag class.
+     * 
+     * @param service The entry point to the Nfc Service for NfcNdefTag class.
+     * @param handle The handle returned by the NFC service and used to identify
+     *            the tag in subsequent calls.
+     * @hide
+     */
+    NdefTag(INfcTag service, int handle) {
+        super(service, handle);
+    }
+
+    /**
+     * Read NDEF data from an NDEF tag.
+     * 
+     * @return the NDEF message read from the tag.
+     * @throws NfcException if the tag is not NDEF-formatted.
+     * @throws IOException if the target has been lost or the connection has
+     *             been closed.
+     * @see NdefMessage
+     */
+    public NdefMessage read() throws NfcException, IOException {
+        // Check state
+        checkState();
+        
+        //Check if the tag is Ndef compliant
+        if(isNdef != true){
+            isNdef = isNdef();
+            if(isNdef != true) {
+                throw new NfcException("Tag is not NDEF compliant");
+            }
+        }
+
+        // Perform transceive
+        try {
+            NdefMessage msg = mService.read(mHandle);
+            if (msg == null) {
+                throw new IOException("NDEF read failed");
+            }
+            return msg;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in read(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Write NDEF data to an NDEF-compliant tag.
+     * 
+     * @param msg NDEF message to be written to the tag.
+     * @throws NfcException if the tag is not NDEF formatted.
+     * @throws IOException if the target has been lost or the connection has
+     *             been closed.
+     * @see NdefMessage
+     */
+    public void write(NdefMessage msg) throws NfcException, IOException {
+        // Check state
+        checkState();
+        
+        //Check if the tag is Ndef compliant
+        if(isNdef != true){
+            isNdef = isNdef();
+            if(isNdef != true) {
+                throw new NfcException("Tag is not NDEF compliant");
+            }
+        }
+
+        // Perform transceive
+        try {
+            boolean isSuccess = mService.write(mHandle, msg);
+            if (!isSuccess) {
+                throw new IOException("NDEF write failed");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in write(): ", e);
+        }
+    }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcException.java b/core/java/com/trustedlogic/trustednfc/android/NfcException.java
new file mode 100644
index 0000000..2497c15
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NfcException.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NFCException.java
+ * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
+ * Created         : 26-08-2009
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+/**
+ * Generic exception thrown in case something unexpected happened during the
+ * NFCManager operations.
+ *
+ * @since AA01.04
+ * @hide
+ */
+public class NfcException extends Exception {
+   /**
+    * Constructs a new NfcException with the current stack trace and the
+    * specified detail message.
+    *
+    * @param s the detail message for this exception.
+    */
+   public NfcException(String s) {
+      super(s);
+   }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcManager.java b/core/java/com/trustedlogic/trustednfc/android/NfcManager.java
new file mode 100644
index 0000000..98ab5bf
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NfcManager.java
@@ -0,0 +1,656 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NfcManager.java
+ * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
+ * Created         : 26-08-2009
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.media.MiniThumbFile;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+//import android.util.Log;
+
+/**
+ * This class provides the primary API for managing all aspects of NFC. Get an
+ * instance of this class by calling
+ * Context.getSystemService(Context.NFC_SERVICE).
+ * @hide
+ */
+public final class NfcManager {
+    /**
+     * Tag Reader Discovery mode
+     */
+    private static final int DISCOVERY_MODE_TAG_READER = 0;
+
+    /**
+     * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an
+     * NFC-IP1 communication. Implementations should not assume that the
+     * controller will end up behaving as an NFC-IP1 target or initiator and
+     * should handle both cases, depending on the type of the remote peer type.
+     */
+    private static final int DISCOVERY_MODE_NFCIP1 = 1;
+
+    /**
+     * Card Emulation mode Enables the manager to act as an NFC tag. Provided
+     * that a Secure Element (an UICC for instance) is connected to the NFC
+     * controller through its SWP interface, it can be exposed to the outside
+     * NFC world and be addressed by external readers the same way they would
+     * with a tag.
+     * <p>
+     * Which Secure Element is exposed is implementation-dependent.
+     * 
+     * @since AA01.04
+     */
+    private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
+
+    /**
+     * Used as Parcelable extra field in
+     * {@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_TAG_DISCOVERED_ACTION}
+     * . It contains the NDEF message read from the NDEF tag discovered.
+     */
+    public static final String NDEF_MESSAGE_EXTRA = "com.trustedlogic.trustednfc.android.extra.NDEF_MESSAGE";
+
+    /**
+     * Broadcast Action: a NDEF tag has been discovered.
+     * <p>
+     * Always contains the extra field
+     * {@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_MESSAGE_EXTRA}.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_NOTIFY permission.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String NDEF_TAG_DISCOVERED_ACTION = "com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED";
+
+    /**
+     * Used as byte array extra field in
+     * {@link com.trustedlogic.trustednfc.android.NfcManager#TRANSACTION_DETECTED_ACTION}
+     * . It contains the AID of the applet concerned by the transaction.
+     */
+    public static final String AID_EXTRA = "com.trustedlogic.trustednfc.android.extra.AID";
+
+    /**
+     * Broadcast Action: a transaction with a secure element has been detected.
+     * <p>
+     * Always contains the extra field
+     * {@link com.trustedlogic.trustednfc.android.NfcManager#AID_EXTRA}
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_NOTIFY permission
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String TRANSACTION_DETECTED_ACTION = "com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED";
+
+    /**
+     * LLCP link status: The LLCP link is activated.
+     * 
+     * @since AA02.01
+     */
+    public static final int LLCP_LINK_STATE_ACTIVATED = 0;
+
+    /**
+     * LLCP link status: The LLCP link is deactivated.
+     * 
+     * @since AA02.01
+     */
+    public static final int LLCP_LINK_STATE_DEACTIVATED = 1;
+
+    /**
+     * Used as int extra field in
+     * {@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_ACTION}
+     * . It contains the new state of the LLCP link.
+     */
+    public static final String LLCP_LINK_STATE_CHANGED_EXTRA = "com.trustedlogic.trustednfc.android.extra.LLCP_LINK_STATE";
+
+    /**
+     * Broadcast Action: the LLCP link state changed.
+     * <p>
+     * Always contains the extra field
+     * {@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_EXTRA}.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_LLCP permission.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String LLCP_LINK_STATE_CHANGED_ACTION = "com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED";
+
+    private static final String TAG = "NfcManager";
+
+    private Handler mHandler;
+
+    private INfcManager mService;
+
+    private INfcTag mNfcTagService;
+
+    private IP2pTarget mP2pTargetService;
+
+    private IP2pInitiator mP2pInitiatorService;
+
+    private ILlcpSocket mLlcpSocketService;
+
+    private ILlcpConnectionlessSocket mLlcpConnectionlessSocketService;
+
+    private ILlcpServiceSocket mLlcpServiceSocketService;
+
+    static NfcException convertErrorToNfcException(int errorCode) {
+        return convertErrorToNfcException(errorCode, null);
+    }
+
+    static NfcException convertErrorToNfcException(int errorCode, String message) {
+        if (message == null) {
+            message = "";
+        } else {
+            message = " (" + message + ")";
+        }
+
+        switch (errorCode) {
+            case ErrorCodes.ERROR_BUSY:
+                return new NfcException("Another operation is already pending" + message);
+            case ErrorCodes.ERROR_CANCELLED:
+                return new NfcException("Operation cancelled" + message);
+            case ErrorCodes.ERROR_TIMEOUT:
+                return new NfcException("Operation timed out" + message);
+            case ErrorCodes.ERROR_SOCKET_CREATION:
+                return new NfcException("Error during the creation of an Llcp socket:" + message);
+            case ErrorCodes.ERROR_SAP_USED:
+                return new NfcException("Error SAP already used:" + message);
+            case ErrorCodes.ERROR_SERVICE_NAME_USED:
+                return new NfcException("Error Service Name already used:" + message);
+            case ErrorCodes.ERROR_SOCKET_OPTIONS:
+                return new NfcException("Error Socket options:" + message);
+            case ErrorCodes.ERROR_INVALID_PARAM:
+                return new NfcException("Error Set Properties: invalid param" + message);
+            case ErrorCodes.ERROR_NFC_ON:
+                return new NfcException("Error Set Properties : NFC is ON" + message);
+            case ErrorCodes.ERROR_NOT_INITIALIZED:
+                return new NfcException("NFC is not running " + message);
+            case ErrorCodes.ERROR_SE_ALREADY_SELECTED:
+                return new NfcException("Secure Element already connected" + message);
+            case ErrorCodes.ERROR_NO_SE_CONNECTED:
+                return new NfcException("No Secure Element connected" + message);
+            case ErrorCodes.ERROR_SE_CONNECTED:
+                return new NfcException("A secure Element is already connected" + message);
+            default:
+                return new NfcException("Unkown error code " + errorCode + message);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public NfcManager(INfcManager service, Handler handler) {
+        mService = service;
+        mHandler = handler;
+        try {
+            mNfcTagService = mService.getNfcTagInterface();
+            mP2pInitiatorService = mService.getP2pInitiatorInterface();
+            mP2pTargetService = mService.getP2pTargetInterface();
+            mLlcpServiceSocketService = mService.getLlcpServiceInterface();
+            mLlcpConnectionlessSocketService = mService.getLlcpConnectionlessInterface();
+            mLlcpSocketService = mService.getLlcpInterface();
+        } catch (RemoteException e) {
+            mLlcpSocketService = null;
+            mNfcTagService = null;
+            mP2pInitiatorService = null;
+            mP2pTargetService = null;
+            mLlcpConnectionlessSocketService = null;
+            mLlcpServiceSocketService = null;
+        }
+    }
+
+    /**
+     * Return the status of the NFC feature
+     * 
+     * @return mIsNfcEnabled
+     * @since AA02.01
+     */
+    public boolean isEnabled() {
+        try {
+            return mService.isEnabled();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in isEnabled(): ", e);
+            return false;
+        }
+    }
+
+    /**
+     * Enable the NFC Feature
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_ADMIN permission
+     * 
+     * @throws NfcException if the enable failed
+     * @since AA02.01
+     */
+    public void enable() throws NfcException {
+        try {
+            boolean isSuccess = mService.enable();
+            if (isSuccess == false) {
+                throw new NfcException("NFC Service failed to enable");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in enable(): ", e);
+        }
+    }
+
+    /**
+     * Disable the NFC feature
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_ADMIN permission
+     * 
+     * @throws NfcException if the disable failed
+     * @since AA02.01
+     */
+    public void disable() throws NfcException {
+        try {
+            boolean isSuccess = mService.disable();
+            if (isSuccess == false) {
+                throw new NfcException("NFC Service failed to disable");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in disable(): ", e);
+        }
+    }
+
+    /**
+     * Get the list of the identifiers of the Secure Elements detected
+     * by the NFC controller.
+     * 
+     * @return list a list of Secure Element identifiers.
+     * @see #getSelectedSecureElement
+     * @see #selectSecureElement(int)
+     * @see #deselectSecureElement
+     * @since AA02.01
+     */
+    public int[] getSecureElementList() {
+        try {
+            return mService.getSecureElementList();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getSecureElementList(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Get the identifier of the currently selected secure element.
+     * 
+     * @return id identifier of the currently selected Secure Element. 0 if none.
+     * @see #getSecureElementList
+     * @see #selectSecureElement(int)
+     * @see #deselectSecureElement
+     * @since AA02.01
+     */
+    public int getSelectedSecureElement() {
+        try {
+            return mService.getSelectedSecureElement();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getSelectedSecureElement(): ", e);
+            return -1;
+        }
+    }
+
+    /**
+     * Select a specific Secure Element by its identifier.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_ADMIN permission
+     * 
+     * @throws NfcException if a or this secure element is already selected
+     * @see #getSecureElementList
+     * @see #getSelectedSecureElement
+     * @see #deselectSecureElement
+     * @since AA02.01
+     */
+    public void selectSecureElement(int seId) throws NfcException  {
+        try {
+           int status = mService.selectSecureElement(seId);
+           if(status != ErrorCodes.SUCCESS){
+               throw convertErrorToNfcException(status);
+           }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in selectSecureElement(): ", e);
+        }
+    }
+
+    /**
+     * Deselect the currently selected Secure Element
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_ADMIN permission
+     * 
+     * @throws NfcException if no secure Element is selected
+     * @see #getSecureElementList
+     * @see #getSelectedSecureElement
+     * @see #selectSecureElement(int)
+     * @since AA02.01
+     */
+    public void deselectSecureElement() throws NfcException {
+        try {
+            int status = mService.deselectSecureElement();
+            if(status != ErrorCodes.SUCCESS){
+                throw convertErrorToNfcException(status);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in deselectSecureElement(): ", e);
+        }
+    }
+
+    /**
+     * Open a connection with a remote NFC peer
+     * 
+     * This method does not return while no remote NFC peer enters the field.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_RAW permission
+     * 
+     * @return P2pDevice object to be used to communicate with the detected
+     *         peer.
+     * @throws IOException if the target has been lost or the connection has
+     *             been closed.
+     * @throws NfcException if an open is already started
+     * @see P2pDevice
+     * @see #getOpenTimeout
+     * @see #setOpenTimeout(int)
+     * @see #cancel
+     * @since AA02.01
+     */
+    public P2pDevice openP2pConnection() throws IOException, NfcException {
+        try {
+            int handle = mService.openP2pConnection();
+            // Handle potential errors
+            if (ErrorCodes.isError(handle)) {
+                if (handle == ErrorCodes.ERROR_IO) {
+                    throw new IOException();
+                } else {
+                    throw convertErrorToNfcException(handle);
+                }
+            }
+            // Build the public NfcTag object, depending on its type
+            if (mP2pTargetService.getMode(handle) == P2pDevice.MODE_P2P_TARGET) {
+                return new P2pTarget(mP2pTargetService, handle);
+            } else {
+                return new P2pInitiator(mP2pInitiatorService, handle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in openTagConnection(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Open a connection with a tag
+     *
+     * This method does not return while no tag enters the field.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_RAW permission
+     * 
+     * @return tag object to be use to communicate with the detected NfcTag.
+     * @throws IOException if the target has been lost or the connection has
+     *             been closed.
+     * @throws NfcException if an open is already started
+     * @see NfcTag
+     * @see #getOpenTimeout
+     * @see #setOpenTimeout(int)
+     * @see #cancel
+     * @since AA02.01
+     */
+    public NfcTag openTagConnection() throws IOException, NfcException {
+        try {
+            int handle = mService.openTagConnection();
+            // Handle potential errors
+            if (ErrorCodes.isError(handle)) {
+                if (handle == ErrorCodes.ERROR_IO) {
+                    throw new IOException();
+                } else {
+                    throw convertErrorToNfcException(handle);
+                }
+            }
+            // Build the public NfcTag object
+            return new NfcTag(mNfcTagService, handle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in openTagConnection(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Set the timeout for open requests
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_RAW permission
+     * 
+     * @param timeout value of the timeout for open request
+     * @see #openP2pConnection
+     * @see #openTagConnection
+     * @see #getOpenTimeout
+     * @since AA02.01
+     */
+    public void setOpenTimeout(int timeout) {
+        try {
+            mService.setOpenTimeout(timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in setOpenTimeout(): ", e);
+        }
+    }
+
+    /**
+     * Get the timeout value of open requests
+     * 
+     * @return mTimeout
+     * @see #setOpenTimeout(int)
+     * @since AA02.01
+     */
+    public int getOpenTimeout() {
+        try {
+            return mService.getOpenTimeout();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getOpenTimeout(): ", e);
+            return 0;
+        }
+    }
+
+    /**
+     * Cancel an openTagConnection or an openP2pConnection started
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_RAW permission
+     * 
+     * @see #openP2pConnection
+     * @see #openTagConnection
+     * @since AA02.01
+     */
+    public void cancel() {
+        try {
+            mService.cancel();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in cancel(): ", e);
+        }
+    }
+
+    /**
+     * Creates a connectionless socket for a LLCP link and set its Service
+     * Access Point number (SAP)
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_LLCP permission
+     * 
+     * @param sap Service Access Point number related to the created
+     *            Connectionless socket.
+     * @return LlcpConnectionlessSocket object to be used in a LLCP
+     *         Connectionless communication.
+     * @throws IOException if the socket creation failed
+     * @throws NfcException if socket ressources are insufficicent
+     * @see LlcpConnectionlessSocket
+     * @since AA02.01
+     */
+    public LlcpConnectionlessSocket createLlcpConnectionlessSocket(int sap) throws IOException,
+            NfcException {
+
+        try {
+            int handle = mService.createLlcpConnectionlessSocket(sap);
+            // Handle potential errors
+            if (ErrorCodes.isError(handle)) {
+                if (handle == ErrorCodes.ERROR_IO) {
+                    throw new IOException();
+                } else {
+                    throw convertErrorToNfcException(handle);
+                }
+            }
+
+            // Build the public LlcpConnectionLess object
+            return new LlcpConnectionlessSocket(mLlcpConnectionlessSocketService, handle);
+
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in createLlcpConnectionlessSocket(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Creates a LlcpServiceSocket for a LLCP link, set its Service Access Point
+     * number (SAP).
+     * <p>
+     * During a LLCP communication, the LlcpServiceSocket will create LlcpSocket
+     * to communicate with incoming LLCP clients. For that, a server socket need
+     * to have some informations as a working buffer length in order to handle
+     * incoming data and some options to define the LLCP communication.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_LLCP permission
+     * 
+     * @param sap
+     * @param sn Service Name of the LlcpServiceSocket
+     * @param miu Maximum Information Unit (MIU) for a LlcpSocket created by the
+     *            LlcpServiceSocket
+     * @param rw Receive Window (RW) for a LlcpSocket created by the
+     *            LlcpServiceSocket
+     * @param linearBufferLength size of the memory space needed to handle
+     *            incoming data for every LlcpSocket created.
+     * @return LlcpServiceSocket object to be used as a LLCP Service in a
+     *         connection oriented communication.
+     * @throws IOException if the socket creation failed
+     * @throws NfcException if socket ressources are insufficicent
+     * @see LlcpServiceSocket
+     * @since AA02.01
+     */
+    public LlcpServiceSocket createLlcpServiceSocket(int sap, String sn, int miu, int rw,
+            int linearBufferLength) throws IOException, NfcException {
+        try {
+            int handle = mService.createLlcpServiceSocket(sap, sn, miu, rw, linearBufferLength);
+            // Handle potential errors
+            if (ErrorCodes.isError(handle)) {
+                if (handle == ErrorCodes.ERROR_IO) {
+                    throw new IOException();
+                } else {
+                    throw convertErrorToNfcException(handle);
+                }
+            }
+
+            // Build the public LlcpServiceSocket object
+            return new LlcpServiceSocket(mLlcpServiceSocketService, mLlcpSocketService, handle);
+
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in createLlcpServiceSocket(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Creates a LlcpSocket for a LLCP link with a specific Service Access Point
+     * number (SAP)
+     * <p>
+     * A LlcpSocket need to have a linear buffer in order to handle incoming
+     * data. This linear buffer will be used to store incoming data as a stream.
+     * Data will be readable later.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_LLCP permission
+     * 
+     * @param sap Service Access Point number for the created socket
+     * @param miu Maximum Information Unit (MIU) of the communication socket
+     * @param rw Receive Window (RW) of the communication socket
+     * @param linearBufferLength size of the memory space needed to handle
+     *            incoming data with this socket
+     * @throws IOException if the socket creation failed
+     * @throws NfcException if socket ressources are insufficicent
+     * @see LlcpSocket
+     * @since AA02.01
+     */
+    public LlcpSocket createLlcpSocket(int sap, int miu, int rw, int linearBufferLength)
+            throws IOException, NfcException {
+        try {
+            int handle = mService.createLlcpSocket(sap, miu, rw, linearBufferLength);
+            // Handle potential errors
+            if (ErrorCodes.isError(handle)) {
+                if (handle == ErrorCodes.ERROR_IO) {
+                    throw new IOException();
+                } else {
+                    throw convertErrorToNfcException(handle);
+                }
+            }
+            // Build the public LlcpSocket object
+            return new LlcpSocket(mLlcpSocketService, handle);
+
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in createLlcpSocket(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Set different parameters like the NCIP General bytes, the LLCP link
+     * parameters and all tag discovery parameters.
+     * <p class="note">
+     * <strong>Note:</strong> Requires the NFC_ADMIN permission
+     * 
+     * @param param parameter to be updated with a new value
+     * @param value new value of the parameter
+     * @throws NfcException if incorrect parameters of NFC is ON
+     * @since AA02.01
+     */
+    public void setProperties(String param, String value) throws NfcException {
+        try {
+            int result = mService.setProperties(param, value);
+            // Handle potential errors
+            if (ErrorCodes.isError(result)) {
+                throw convertErrorToNfcException(result);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in setProperties(): ", e);
+        }
+    }
+
+    /**
+     * Get the value of different parameters like the NCFIP General bytes, the
+     * LLCP link parameters and all tag discovery parameters.
+     * 
+     * @param param parameter to be updated
+     * @return String value of the requested parameter
+     * @throws RemoteException
+     * @since AA02.01
+     */
+    public String getProperties(String param) {
+        String value;
+        try {
+            value = mService.getProperties(param);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getProperties(): ", e);
+            return null;
+        }
+        return value;
+    }
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcTag.java b/core/java/com/trustedlogic/trustednfc/android/NfcTag.java
new file mode 100644
index 0000000..798c7e4
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/NfcTag.java
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NFCTag.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ * Created         : 26-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+/**
+ * This class represents tags with no known formatting. One can use the method
+ * {@link #isNdef()} to determine if the tag can store NDEF-formatted messages.
+ * <p>
+ * 
+ * <pre class="prettyprint">
+ * if (tag.isNdef()) {
+ *     NdefTag ndefTag = (NdefTag) tag;
+ *     NdefMessage msg = ndefTag.read();
+ * }
+ * </pre>
+ * 
+ * @since AA01.04
+ * @see NdefMessage
+ * @hide
+ */
+public class NfcTag {
+
+    private static final String TAG = "NfcTag";
+
+    /**
+     * The handle returned by the NFC service and used to identify the tag in
+     * every call of this class.
+     * 
+     * @hide
+     */
+    protected int mHandle;
+
+    /**
+     * The entry point for tag operations.
+     * 
+     * @hide
+     */
+    protected INfcTag mService;
+
+    /**
+     * Flag set when the object is closed and thus not usable any more.
+     * 
+     * @hide
+     */
+    protected boolean isClosed = false;
+
+    /**
+     * Flag set when the tag is connected.
+     * 
+     * @hide
+     */
+    protected boolean isConnected = false;
+    
+    /**
+     * Flag set when a check NDEF is performed.
+     * 
+     * @hide
+     */
+    protected boolean isNdef = false;
+
+    /**
+     * Check if tag is still opened.
+     * 
+     * @return data sent by the P2pInitiator.
+     * @throws NfcException if accessing a closed target.
+     * 
+     * @hide               
+     */
+    public void checkState() throws NfcException {
+        if (isClosed) {
+            throw new NfcException("Tag has been closed.");
+        }
+        if (!isConnected) {
+            throw new NfcException("Tag is not connected.");
+        }
+    }
+
+    /**
+     * Internal constructor for the NfcTag class.
+     * 
+     * @param service The entry point to the Nfc Service for NfcTag class.
+     * @param handle The handle returned by the NFC service and used to identify
+     *            the tag in subsequent calls.
+     * @hide
+     */
+    NfcTag(INfcTag service, int handle) {
+        this.mService = service;
+        this.mHandle = handle;
+    }
+
+    /**
+     * Connects to the tag. This shall be called prior to any other operation on
+     * the tag.
+     * 
+     * @throws IOException if the tag has been lost or the connection has been
+     *             closed.
+     * @throws nfcException if the tag is already in connected state.
+     */
+    public void connect() throws NfcException, IOException {
+        // Check state
+        if (isClosed) {
+            throw new NfcException("Tag has been closed.");
+        }
+        if (isConnected) {
+            throw new NfcException("Already connected");
+        }
+
+        // Perform connect
+        try {
+            int result = mService.connect(mHandle);
+            if (ErrorCodes.isError(result)) {
+                if (result == ErrorCodes.ERROR_IO) {
+                    throw new IOException("Failed to connect");
+                }
+                else {
+                    throw NfcManager.convertErrorToNfcException(result);
+                }
+            }
+            isConnected = true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in connect(): ", e);
+        }
+    }
+
+    /**
+     * Disconnects from the tag. This must be called so that other targets can
+     * be discovered. It restarts the NFC discovery loop.
+     * 
+     * @throws NfcException if the tag is already in disconnected state or not connected
+     */
+    public void close() throws NfcException {
+        // Check state
+        checkState();
+
+        try {
+            mService.close(mHandle);
+            isClosed = true;
+            isConnected = false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in close(): ", e);
+        }
+    }
+
+    /**
+     * Exchanges raw data with the tag, whatever the tag type.
+     * 
+     * To exchange APDUs with a ISO14443-4-compliant tag, the data parameter
+     * must be filled with the C-APDU (CLA, INS, P1, P2 [, ...]). The returned
+     * data consists of the R-APDU ([...,] SW1, SW2).
+     * 
+     * @param data data to be sent to the tag
+     * @return data sent in response by the tag
+     * @throws IOException if the tag has been lost or the connection has been
+     *             closed.
+     * @throws NfcException in case of failure within the stack
+     */
+    public byte[] transceive(byte[] data) throws IOException, NfcException {
+        // Check state
+        checkState();
+
+        // Perform transceive
+        try {
+            byte[] response = mService.transceive(mHandle, data);
+            if (response == null) {
+                throw new IOException("Transceive failed");
+            }
+            return response;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in transceive(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Checks whether tag is NDEF-compliant or not.
+     * 
+     * @return true if the tag is NDEF-compliant, false otherwise
+     * @throws NfcException in case an error occurred when trying to determine
+     *             whether the tag is NDEF-compliant
+     */
+    public boolean isNdef() throws NfcException {
+        // Check state
+        checkState();
+
+        // Perform Check Ndef
+        try {
+            isNdef = mService.isNdef(mHandle);
+            return isNdef;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in isNdef(): ", e);
+            return false;
+        }
+    }
+
+    /**
+     * Returns target type. constants.
+     * 
+     * @return tag type.
+     */
+    public String getType() {
+        try {
+            return mService.getType(mHandle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getType(): ", e);
+            return null;
+        }
+    }
+    
+    /**
+     * Returns target UID.
+     * 
+     * @return tag UID.
+     */
+    public byte[] getUid() {
+        try {
+            return mService.getUid(mHandle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getType(): ", e);
+            return null;
+        }
+    }
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pDevice.java b/core/java/com/trustedlogic/trustednfc/android/P2pDevice.java
new file mode 100644
index 0000000..65800f2
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/P2pDevice.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : P2PDevice.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ * Created         : 26-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+/**
+ * P2pDevice is the abstract base class for all supported P2P targets the
+ * NfcManager can handle.
+ * @hide
+ */
+public abstract class P2pDevice {
+
+    /**
+     * Peer-to-Peer Target.
+     */
+    public static final short MODE_P2P_TARGET = 0x00;
+
+    /**
+     * Peer-to-Peer Initiator.
+     */
+    public static final short MODE_P2P_INITIATOR = 0x01;
+
+    /**
+     * Invalid target type.
+     */
+    public static final short MODE_INVALID = 0xff;
+
+    /**
+     * Target handle, used by native calls.
+     * @hide
+     */
+    protected int mHandle;
+	
+    /**
+     * Flag set when the object is closed and thus not usable any more.
+     * @hide
+     */
+	protected boolean isClosed = false;
+
+    /**
+     * Prevent default constructor to be public.
+     * @hide
+     */
+	protected P2pDevice() {
+	}
+
+	/**
+     * Returns the remote NFC-IP1 General Bytes.
+     * 
+     * @return remote general bytes
+	 * @throws IOException 
+     */
+    public byte[] getGeneralBytes() throws IOException {
+        // Should not be called directly (use subclasses overridden method instead)
+        return null;
+    }
+
+    /**
+     * Returns target type. The value returned can be one of the TYPE_*
+     * constants.
+     * 
+     * @return target type.
+     */
+    public int getMode() {
+        // Should not be called directly (use subclasses overridden method instead)
+        return MODE_INVALID;
+    }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java b/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java
new file mode 100644
index 0000000..0f28ae0
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : P2PInitiator.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * P2pInitiator represents the initiator in an NFC-IP1 peer-to-peer
+ * communication.
+ * 
+ * @see P2pTarget
+ * @since AA02.01
+ * @hide
+ */
+public class P2pInitiator extends P2pDevice {
+
+    private static final String TAG = "P2pInitiator";
+
+	/**
+     * The entry point for P2P tag operations.
+     * @hide
+     */
+	private IP2pInitiator mService;
+	
+    /**
+     * Internal constructor for the P2pInitiator class.
+     * 
+     * @param handle The handle returned by the NFC service and used to identify
+     * 				 the tag in subsequent calls.
+     * 
+     * @hide
+     */
+    P2pInitiator(IP2pInitiator service, int handle) {
+        this.mService = service;
+        this.mHandle = handle;
+    }	
+
+    /**
+     * Receives data from a P2pInitiator.
+     * 
+     * @return data sent by the P2pInitiator.
+     * @throws IOException if the target has been lost or if the connection has
+     *             been closed.
+     */
+    public byte[] receive() throws IOException {
+        try {
+        	byte[] result = mService.receive(mHandle);
+        	if (result == null) {
+        		throw new IOException("Tag has been lost");
+        	}
+            return result;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in receive(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Sends data to a P2pInitiator.
+     * 
+     * @param data data to be sent to the P2pInitiator.
+     * @throws IOException if the target has been lost or if the connection has
+     *             been closed.
+     */
+    public void send(byte[] data) throws IOException {
+        try {
+        	boolean isSuccess = mService.send(mHandle, data);
+        	if (!isSuccess) {
+        		throw new IOException("Tag has been lost");
+        	}
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in send(): ", e);
+        }
+    }
+
+    @Override
+    public byte[] getGeneralBytes() {
+        try {
+            return mService.getGeneralBytes(mHandle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getGeneralBytes(): ", e);
+            return null;
+        }
+    }
+
+    @Override
+    public int getMode() {
+        return P2pDevice.MODE_P2P_INITIATOR;
+    }
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java b/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java
new file mode 100644
index 0000000..b5e00db
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : P2PTarget.java
+ * Original-Author : Trusted Logic S.A. (Daniel Tomas)
+ */
+
+package com.trustedlogic.trustednfc.android;
+
+import java.io.IOException;
+
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * P2pTarget represents the target in an NFC-IP1 peer-to-peer communication.
+ * 
+ * @see P2pInitiator
+ * @since AA02.01
+ * @hide
+ */
+public class P2pTarget extends P2pDevice {
+
+    private static final String TAG = "P2pTarget";
+
+	/**
+     * The entry point for P2P tag operations.
+     * @hide
+     */
+	private IP2pTarget mService;
+	
+    /**
+     * Flag set when the object is closed and thus not usable any more.
+     * @hide
+     */
+	private boolean isClosed = false;
+	
+    /**
+     * Flag set when the tag is connected.
+     * @hide
+     */
+	private boolean isConnected = false;
+	
+    /**
+     * Check if tag is still opened.
+     * 
+     * @return data sent by the P2pInitiator.
+     * @throws NfcException if accessing a closed target.
+     * 
+     * @hide          
+     */
+    public void checkState() throws NfcException {
+    	if(isClosed) {
+    		throw new NfcException("Tag has been closed.");
+    	}
+    }
+
+    /**
+     * Internal constructor for the P2pTarget class.
+     * 
+     * @param handle The handle returned by the NFC service and used to identify
+     * 				 the tag in subsequent calls.
+     * 
+     * @hide
+     */
+    P2pTarget(IP2pTarget service, int handle) {
+        this.mService = service;
+        this.mHandle = handle;
+    }	
+
+    /**
+     * Connects to the P2pTarget. This shall be called prior to any other
+     * operation on the P2pTarget.
+     * 
+     * @throws NfcException
+     */
+    public void connect() throws NfcException {
+    	// Check state
+    	checkState();
+    	if (isConnected) {
+    		throw new NfcException("Already connected");
+    	}
+    	
+    	// Perform connect
+        try {
+            int result = mService.connect(mHandle);
+            if (ErrorCodes.isError(result)) {
+                if (result == ErrorCodes.ERROR_IO) {
+                    throw new NfcException("Failed to connect");
+                }
+                else {
+                    throw NfcManager.convertErrorToNfcException(result);
+                }
+            }
+            isConnected = true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in connect(): ", e);
+        }
+    }
+
+    /**
+     * Disconnects from the P2p Target. This must be called so that other
+     * targets can be discovered. It restarts the NFC discovery loop.
+     * 
+     * @throws NFCException
+     */
+    public void disconnect() throws NfcException {
+        checkState();
+        try {
+            mService.disconnect(mHandle);
+            isConnected = true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in disconnect(): ", e);
+        }
+    }
+
+    /**
+     * Exchanges raw data with the P2pTarget.
+     * 
+     * @param data data to be sent to the P2pTarget
+     * @return data sent in response by the P2pTarget
+     * @throws IOException if the target has been lost or the connection has
+     *             been closed.
+     * @throws NfcException in case of failure within the stack
+     */
+    public byte[] transceive(byte[] data) throws IOException, NfcException {
+    	// Check state
+    	checkState();
+
+    	// Perform transceive
+        try {
+            byte[] response = mService.transceive(mHandle, data);
+            if (response == null) {
+                throw new IOException("Transceive failed");
+            }
+            return response;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in transceive(): ", e);
+            return null;
+        }
+    }
+
+    /**
+     * Get the General bytes of the connected P2P Target
+     * 
+     * @return general bytes of the connected P2P Target
+     * @throws IOException if the target in not in connected state
+     */
+    public byte[] getGeneralBytes() throws IOException {
+        try {
+            if(isConnected){
+                return mService.getGeneralBytes(mHandle);
+            }else{
+                throw new IOException("Target not in connected state");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getGeneralBytes(): ", e);
+            return null;
+        }
+    }
+
+    @Override
+    public int getMode() {
+        return P2pDevice.MODE_P2P_TARGET;
+    }
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java b/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java
new file mode 100644
index 0000000..ca3b7e0
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * File            : ErrorCodes.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 26-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+/**
+ * This class defines all the error codes that can be returned by the service
+ * and producing an exception on the application level. These are needed since
+ * binders does not support exceptions.
+ * 
+ * @hide
+ */
+public class ErrorCodes {
+
+    public static boolean isError(int code) {
+        if (code < 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static final int SUCCESS = 0;
+    
+    public static final int ERROR_IO = -1;
+
+    public static final int ERROR_CANCELLED = -2;
+
+    public static final int ERROR_TIMEOUT = -3;
+
+    public static final int ERROR_BUSY = -4;
+
+    public static final int ERROR_CONNECT = -5;
+
+    public static final int ERROR_DISCONNECT = -5;
+
+    public static final int ERROR_READ = -6;
+
+    public static final int ERROR_WRITE = -7;
+
+    public static final int ERROR_INVALID_PARAM = -8;
+    
+    public static final int ERROR_INSUFFICIENT_RESOURCES = -9;
+    
+    public static final int ERROR_SOCKET_CREATION = -10;
+    
+    public static final int ERROR_SOCKET_NOT_CONNECTED = -11;
+    
+    public static final int ERROR_BUFFER_TO_SMALL = -12;
+
+    public static final int ERROR_SAP_USED = -13;
+    
+    public static final int ERROR_SERVICE_NAME_USED = -14;
+    
+    public static final int ERROR_SOCKET_OPTIONS = -15;
+    
+    public static final int ERROR_NFC_ON = -16;
+    
+    public static final int ERROR_NOT_INITIALIZED = -17;
+    
+    public static final int ERROR_SE_ALREADY_SELECTED = -18;
+    
+    public static final int ERROR_SE_CONNECTED = -19;
+    
+    public static final int ERROR_NO_SE_CONNECTED = -20;
+    
+    
+    
+    
+    
+    
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java
new file mode 100644
index 0000000..ccfbeb4
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NativeLlcpConnectionLessSocket.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+import com.trustedlogic.trustednfc.android.LlcpPacket;
+
+/**
+ * LlcpConnectionlessSocket represents a LLCP Connectionless object to be used
+ * in a connectionless communication
+ * 
+ * @since AA02.01
+ * {@hide}
+ */
+
+public class NativeLlcpConnectionlessSocket {
+	
+	private int mHandle;
+	
+	private int mSap;
+	
+	private int mLinkMiu;
+	
+	public NativeLlcpConnectionlessSocket(){;
+	}
+	
+	public NativeLlcpConnectionlessSocket(int sap){
+		mSap = sap;
+	}
+	
+    public native boolean doSendTo(int sap, byte[] data);
+
+    public native LlcpPacket doReceiveFrom(int linkMiu);
+
+    public native boolean doClose();
+    
+    public int getLinkMiu(){
+    	return mLinkMiu;
+    }
+    
+    public int getSap(){
+    	return mSap;
+    }
+    
+    public int getHandle(){
+    	return mHandle;
+    }
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java
new file mode 100644
index 0000000..a01f135
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NativeLlcpServerSocket.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+/**
+ * LlcpServiceSocket represents a LLCP Service to be used in a
+ * Connection-oriented communication
+ * {@hide}
+ */
+
+public class NativeLlcpServiceSocket {
+
+	private int mHandle;
+	
+	private int mLocalMiu;
+	
+	private int mLocalRw;
+	
+	private int mLocalLinearBufferLength;
+	
+	private int mSap;
+	
+	private int mTimeout;
+	
+	private String mServiceName;
+	
+	public NativeLlcpServiceSocket(){
+		
+	}
+	
+	public NativeLlcpServiceSocket(String serviceName){
+		mServiceName = serviceName;
+	}
+	
+    public native NativeLlcpSocket doAccept(int timeout, int miu, int rw, int linearBufferLength);
+
+    public native boolean doClose();
+    
+    public int getHandle(){
+    	return mHandle;
+    }
+    
+    public void setAcceptTimeout(int timeout){
+    	mTimeout = timeout; 
+    }
+    
+    public int getAcceptTimeout(){
+    	return mTimeout;
+    }
+    
+    public int getRw(){
+    	return mLocalRw;
+    }
+    
+    public int getMiu(){
+    	return mLocalMiu;
+    }
+    
+    public int getLinearBufferLength(){
+    	return mLocalLinearBufferLength;
+    }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java
new file mode 100644
index 0000000..077c5e0
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NativeLlcpClientSocket.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+/**
+ * LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a
+ * connection-oriented communication
+ * {@hide}
+ */
+
+public class NativeLlcpSocket {
+	
+	private int mHandle;
+	
+	private int mSap;
+	
+	private int mLocalMiu;
+	
+	private int mLocalRw;
+	
+    private int mTimeout;
+    
+    public NativeLlcpSocket(){
+    	
+    }
+    
+    public NativeLlcpSocket(int sap, int miu, int rw){
+    	mSap = sap;
+    	mLocalMiu = miu;
+    	mLocalRw = rw;
+    }
+
+    public native boolean doConnect(int nSap, int timeout);
+
+    public native boolean doConnectBy(String sn, int timeout);
+
+    public native boolean doClose();
+
+    public native boolean doSend(byte[] data);
+
+    public native int doReceive(byte[] recvBuff);
+    
+    public native int doGetRemoteSocketMiu();
+    
+    public native int doGetRemoteSocketRw();
+    
+    
+    
+    public void setConnectTimeout(int timeout){
+    	mTimeout = timeout;
+    }
+    
+    public int getConnectTimeout(){
+    	return mTimeout;
+    }
+    
+    public int getSap(){
+    	return mSap;
+    }
+    
+    public int getMiu(){
+    	return mLocalMiu;
+    }
+    
+    public int getRw(){
+    	return mLocalRw;
+    }
+    
+    public int getHandle(){
+    	return mHandle;
+    }
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java
new file mode 100644
index 0000000..d1e64a6
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NativeNdefTag.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+/**
+ * Native interface to the NDEF tag functions
+ *
+ * {@hide}
+ */
+public class NativeNdefTag {
+    private int mHandle;
+
+    public native byte[] doRead();
+
+    public native boolean doWrite(byte[] buf);
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
new file mode 100644
index 0000000..2f5a0f09
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NativeNfcManager.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import com.trustedlogic.trustednfc.android.NfcManager;
+import com.trustedlogic.trustednfc.android.NdefMessage;
+import com.trustedlogic.trustednfc.android.NfcTag;
+
+/**
+ * Native interface to the NFC Manager functions {@hide}
+ */
+public class NativeNfcManager {
+    
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA = "com.trustedlogic.trustednfc.android.extra.INTERNAL_LLCP_LINK_STATE";
+
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION = "com.trustedlogic.trustednfc.android.action.INTERNAL_LLCP_LINK_STATE_CHANGED";
+    
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String INTERNAL_TARGET_DESELECTED_ACTION = "com.trustedlogic.trustednfc.android.action.INTERNAL_TARGET_DESELECTED";
+
+    /* Native structure */
+    private int mNative;
+
+    private Context mContext;
+
+    private Handler mNfcHandler;
+
+    private static final String TAG = "NativeNfcManager";
+
+    private static final int MSG_NDEF_TAG = 0;
+
+    private static final int MSG_CARD_EMULATION = 1;
+
+    private static final int MSG_LLCP_LINK_ACTIVATION = 2;
+
+    private static final int MSG_LLCP_LINK_DEACTIVATED = 3;
+
+    private static final int MSG_TARGET_DESELECTED = 4;
+
+    public NativeNfcManager(Context context) {
+        mNfcHandler = new NfcHandler();
+        mContext = context;
+    }
+
+    /**
+     * Initializes Native structure
+     */
+    public native boolean initializeNativeStructure();
+
+    /**
+     * Initializes NFC stack.
+     */
+    public native boolean initialize();
+
+    /**
+     * Deinitializes NFC stack.
+     */
+    public native boolean deinitialize();
+
+    /**
+     * Enable discory for the NdefMessage and Transaction notification
+     */
+    public native void enableDiscovery(int mode);
+
+    /**
+     * Disables an NFCManager mode of operation. Allows to disable tag reader,
+     * peer to peer initiator or target modes.
+     * 
+     * @param mode discovery mode to enable. Must be one of the provided
+     *            NFCManager.DISCOVERY_MODE_* constants.
+     */
+    public native void disableDiscoveryMode(int mode);
+
+    public native int[] doGetSecureElementList();
+
+    public native void doSelectSecureElement(int seID);
+
+    public native void doDeselectSecureElement(int seID);
+
+    public native NativeP2pDevice doOpenP2pConnection(int timeout);
+
+    public native NativeNfcTag doOpenTagConnection(int timeout);
+
+    public native int doGetLastError();
+
+    public native void doSetProperties(int param, int value);
+
+    public native void doCancel();
+
+    public native NativeLlcpConnectionlessSocket doCreateLlcpConnectionlessSocket(int nSap);
+
+    public native NativeLlcpServiceSocket doCreateLlcpServiceSocket(int nSap, String sn, int miu,
+            int rw, int linearBufferLength);
+
+    public native NativeLlcpSocket doCreateLlcpSocket(int sap, int miu, int rw,
+            int linearBufferLength);
+
+    public native boolean doCheckLlcp();
+
+    public native boolean doActivateLlcp();
+
+    private class NfcHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+
+            try {
+                switch (msg.what) {
+                    case MSG_NDEF_TAG:
+                        Log.d(TAG, "Checking for NDEF tag message");
+                        NativeNfcTag tag = (NativeNfcTag) msg.obj;
+                        if (tag.doConnect()) {
+                            if (tag.checkNDEF()) {
+                                byte[] buff = tag.doRead();
+                                if (buff != null) {
+                                    NdefMessage msgNdef = new NdefMessage(buff);
+                                    if (msgNdef != null) {
+                                        /* Send broadcast ordered */
+                                        Intent NdefMessageIntent = new Intent();
+                                        NdefMessageIntent
+                                                .setAction(NfcManager.NDEF_TAG_DISCOVERED_ACTION);
+                                        NdefMessageIntent.putExtra(NfcManager.NDEF_MESSAGE_EXTRA,
+                                                msgNdef);
+                                        Log.d(TAG, "NDEF message found, broadcasting to applications");
+                                        mContext.sendOrderedBroadcast(NdefMessageIntent,
+                                                android.Manifest.permission.NFC_NOTIFY);
+                                        /* Disconnect tag */
+                                        tag.doAsyncDisconnect();
+                                    }
+                                } else {
+                                   Log.w(TAG, "Unable to read NDEF message (tag empty or not well formated)");
+                                    /* Disconnect tag */
+                                    tag.doAsyncDisconnect();
+                                }
+                            } else {
+                                Log.d(TAG, "Tag is *not* NDEF compliant");
+                                /* Disconnect tag */
+                                tag.doAsyncDisconnect();
+                            }
+                        } else {
+                            /* Disconnect tag */
+                            tag.doAsyncDisconnect();
+                        }
+                        break;
+                    case MSG_CARD_EMULATION:
+                        Log.d(TAG, "Card Emulation message");
+                        byte[] aid = (byte[]) msg.obj;
+                        /* Send broadcast ordered */
+                        Intent TransactionIntent = new Intent();
+                        TransactionIntent.setAction(NfcManager.TRANSACTION_DETECTED_ACTION);
+                        TransactionIntent.putExtra(NfcManager.AID_EXTRA, aid);
+                        Log.d(TAG, "Broadcasting Card Emulation event");
+                        mContext.sendOrderedBroadcast(TransactionIntent,
+                                android.Manifest.permission.NFC_NOTIFY);
+                        break;
+
+                    case MSG_LLCP_LINK_ACTIVATION:
+                        NativeP2pDevice device = (NativeP2pDevice) msg.obj;
+
+                        Log.d(TAG, "LLCP Activation message");
+
+                        if (device.getMode() == NativeP2pDevice.MODE_P2P_TARGET) {
+                            if (device.doConnect()) {
+                                /* Check Llcp compliancy */
+                                if (doCheckLlcp()) {
+                                    /* Activate Llcp Link */
+                                    if (doActivateLlcp()) {
+                                        Log.d(TAG, "Initiator Activate LLCP OK");
+                                        /* Broadcast Intent Link LLCP activated */
+                                        Intent LlcpLinkIntent = new Intent();
+                                        LlcpLinkIntent
+                                                .setAction(INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION);
+                                        LlcpLinkIntent.putExtra(
+                                                INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA,
+                                                NfcManager.LLCP_LINK_STATE_ACTIVATED);
+                                        Log.d(TAG, "Broadcasting internal LLCP activation");
+                                        mContext.sendBroadcast(LlcpLinkIntent);
+                                    }
+
+                                } else {
+                                    device.doDisconnect();
+                                }
+
+                            }
+
+                        } else if (device.getMode() == NativeP2pDevice.MODE_P2P_INITIATOR) {
+                            /* Check Llcp compliancy */
+                            if (doCheckLlcp()) {
+                                /* Activate Llcp Link */
+                                if (doActivateLlcp()) {
+                                    Log.d(TAG, "Target Activate LLCP OK");
+                                    /* Broadcast Intent Link LLCP activated */
+                                    Intent LlcpLinkIntent = new Intent();
+                                    LlcpLinkIntent
+                                            .setAction(INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION);
+                                    LlcpLinkIntent.putExtra(INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA,
+                                            NfcManager.LLCP_LINK_STATE_ACTIVATED);
+                                    Log.d(TAG, "Broadcasting internal LLCP activation");
+                                    mContext.sendBroadcast(LlcpLinkIntent);
+                                }
+                            }
+                        }
+                        break;
+
+                    case MSG_LLCP_LINK_DEACTIVATED:
+                        /* Broadcast Intent Link LLCP activated */
+                        Log.d(TAG, "LLCP Link Deactivated message");
+                        Intent LlcpLinkIntent = new Intent();
+                        LlcpLinkIntent.setAction(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION);
+                        LlcpLinkIntent.putExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA,
+                                NfcManager.LLCP_LINK_STATE_DEACTIVATED);
+                        Log.d(TAG, "Broadcasting LLCP deactivation");
+                        mContext.sendOrderedBroadcast(LlcpLinkIntent,
+                                android.Manifest.permission.NFC_LLCP);
+                        break;
+
+                    case MSG_TARGET_DESELECTED:
+                        /* Broadcast Intent Target Deselected */
+                        Log.d(TAG, "Target Deselected");
+                        Intent TargetDeselectedIntent = new Intent();
+                        TargetDeselectedIntent.setAction(INTERNAL_TARGET_DESELECTED_ACTION);
+                        Log.d(TAG, "Broadcasting Intent");
+                        mContext.sendOrderedBroadcast(TargetDeselectedIntent,
+                                android.Manifest.permission.NFC_LLCP);
+                        break;
+
+                    default:
+                        Log.e(TAG, "Unknown message received");
+                        break;
+                }
+            } catch (Exception e) {
+                // Log, don't crash!
+                Log.e(TAG, "Exception in NfcHandler.handleMessage:", e);
+            }
+        }
+    };
+
+    /**
+     * Notifies Ndef Message
+     */
+    private void notifyNdefMessageListeners(NativeNfcTag tag) {
+        Message msg = mNfcHandler.obtainMessage();
+
+        msg.what = MSG_NDEF_TAG;
+        msg.obj = tag;
+
+        mNfcHandler.sendMessage(msg);
+    }
+
+    /**
+     * Notifies transaction
+     */
+    private void notifyTargetDeselected() {
+        Message msg = mNfcHandler.obtainMessage();
+
+        msg.what = MSG_TARGET_DESELECTED;
+
+        mNfcHandler.sendMessage(msg);
+    }
+
+    /**
+     * Notifies transaction
+     */
+    private void notifyTransactionListeners(byte[] aid) {
+        Message msg = mNfcHandler.obtainMessage();
+
+        msg.what = MSG_CARD_EMULATION;
+        msg.obj = aid;
+
+        mNfcHandler.sendMessage(msg);
+    }
+
+    /**
+     * Notifies P2P Device detected, to activate LLCP link
+     */
+    private void notifyLlcpLinkActivation(NativeP2pDevice device) {
+        Message msg = mNfcHandler.obtainMessage();
+
+        msg.what = MSG_LLCP_LINK_ACTIVATION;
+        msg.obj = device;
+
+        mNfcHandler.sendMessage(msg);
+    }
+
+    /**
+     * Notifies P2P Device detected, to activate LLCP link
+     */
+    private void notifyLlcpLinkDeactivated() {
+        Message msg = mNfcHandler.obtainMessage();
+
+        msg.what = MSG_LLCP_LINK_DEACTIVATED;
+
+        mNfcHandler.sendMessage(msg);
+    }
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java
new file mode 100644
index 0000000..b92783d
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NativeNfcTag.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+/**
+ * Native interface to the NFC tag functions
+ * 
+ * {@hide}
+ */
+public class NativeNfcTag {
+	private int mHandle;
+
+	private String mType;
+	
+	private byte[] mUid;
+
+	public native boolean doConnect();
+
+	public native boolean doDisconnect();
+	
+	public native void doAsyncDisconnect();
+
+	public native byte[] doTransceive(byte[] data);
+
+	public native boolean checkNDEF();
+	
+    public native byte[] doRead();
+
+    public native boolean doWrite(byte[] buf);
+
+	public int getHandle() {
+		return mHandle;
+	}
+	
+	public String getType() {
+		return mType;
+	}
+	
+	public byte[] getUid() {
+		return mUid;
+	}
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java
new file mode 100644
index 0000000..75d25ba
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+/**
+ * File            : NativeP2pDevice.java
+ * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
+ * Created         : 18-02-2010
+ */
+
+package com.trustedlogic.trustednfc.android.internal;
+
+/**
+ * Native interface to the P2P Initiator functions
+ * 
+ * {@hide}
+ */
+public class NativeP2pDevice {
+	
+	/**
+	* Peer-to-Peer Target.
+	*/
+	public static final short MODE_P2P_TARGET          = 0x00;
+
+	/**
+	* Peer-to-Peer Initiator.
+	*/
+	public static final short MODE_P2P_INITIATOR       = 0x01;
+
+	/**
+	* Invalid target type.
+	*/
+	public static final short MODE_INVALID			   = 0xff;
+
+	private int mHandle;
+
+	private int mMode;
+
+	private byte[] mGeneralBytes;
+
+	public native byte[] doReceive();
+
+	public native boolean doSend(byte[] data);
+
+	public native boolean doConnect();
+
+	public native boolean doDisconnect();
+
+	public native byte[] doTransceive(byte[] data);
+	
+	public int getHandle() {
+		return mHandle;
+	}
+
+	public int getMode() {
+		return mMode;
+	}
+
+	public byte[] getGeneralBytes() {
+		return mGeneralBytes;
+	}
+
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/package.html b/core/java/com/trustedlogic/trustednfc/android/package.html
new file mode 100644
index 0000000..0c0b605
--- /dev/null
+++ b/core/java/com/trustedlogic/trustednfc/android/package.html
@@ -0,0 +1,473 @@
+<html>
+<body>
+
+<p>Provides classes that manage the NFC functionality.</p>
+
+<p>The NFC functionality is related to Near Field Communication.</p>
+
+<p>The NFC APIs let applications:</p>
+<ul>
+  <li>Scan for remote NFC targets (NFC Tag or NFC Peer)</li>
+  <li>Transfer raw data to and from remote NFC targets (NFC Tags or NFC Peer)</li>
+  <li>Read/Write NDEF data from/to remote NFC targets (NFC Tags)</li>
+  <li>Establish LLCP connection with a remote NFC target (NFC Peer with LLCP support)</li>
+  <li>Exchange data with a remote NFC target through LLCP services (NFC Peer with LLCP support)</li>
+  <li>Be notified of transactions on the local Secure Element by an external NFC reader</li>
+</ul>
+
+
+<h1>Setting Up NFC</h1>
+
+<p>
+Before an application can use the NFC feature, it needs to check if NFC is
+supported on the device by getting an instance of the
+{@link com.trustedlogic.trustednfc.android.NfcManager} class.
+</p>
+
+<pre>
+	NfcManager mNfcManager = (NfcManager) getSystemService(Context.NFC_SERVICE);
+	if (mNfcManager == null) {
+		// Device does not support NFC
+	}
+</pre>
+
+<p>
+An application can ensure that NFC is enabled.
+If not, an application with the needed permission can request that NFC be
+enabled.
+</p>
+
+<pre>
+	if (!mNfcManager.isEnabled) {
+		// NFC is currently disabled.
+		// Enable NFC.
+		mNfcManager.enable();
+	}
+</pre>
+
+<p>
+Before using the card emulation mode, an application can ensure that a secure
+element is selected ({@link com.trustedlogic.trustednfc.android.NfcManager#getSelectedSecureElement}).
+If not, an application with the needed permission can recover the list of
+available secure elements on the device
+({@link com.trustedlogic.trustednfc.android.NfcManager#getSecureElementList}) and select one
+({@link com.trustedlogic.trustednfc.android.NfcManager#selectSecureElement}).
+</p>
+
+<p>
+Before using the NFC feature, an application can configure the NFC device by
+calling {@link com.trustedlogic.trustednfc.android.NfcManager#setProperties}. This function allows:
+</p>
+<ul>
+  <li>Enabling/disabling the NFC device capabilities (RF types, baudrates,
+  NFCIP-1 mode and role...)</li>
+  <li>Settings the NFCIP-1 general bytes and the LLCP link parameters</li>
+</ul>
+<p>
+The setting properties can be customized according to the Device capabilities.
+The next table give the minimal set of properties supported by the Device.
+Depending on the implementation, the table may be completed.
+</p>
+<table>
+  <TR><TH> Property Name </TH><TH> Property Values </TH></TR>
+  <TR><TD> discovery.felica    </TD><TD> <b>true</b>|false </TD></TR>
+  <TR><TD> discovery.iso14443A </TD><TD> <b>true</b>|false </TD></TR>
+  <TR><TD> discovery.iso14443B </TD><TD> <b>true</b>|false </TD></TR>
+  <TR><TD> discovery.iso15693  </TD><TD> <b>true</b>|false </TD></TR>
+  <TR><TD> discovery.nfcip     </TD><TD> <b>true</b>|false </TD></TR>
+  <TR><TD> nfcip.baudrate     </TD><TD> 106|212|424 </TD></TR>
+  <TR><TD> nfcip.generalbytes </TD><TD>  </TD></TR>
+  <TR><TD> nfcip.mode         </TD><TD> active|passive|<b>all</b> </TD></TR>
+  <TR><TD> nfcip.role         </TD><TD> initiator|target|<b>both</b> </TD></TR>
+  <TR><TD> llcp.lto </TD><TD> <b>150</b> (0 to 255) </TD></TR>
+  <TR><TD> llcp.opt </TD><TD>   <b>0</b> (0 to 3) </TD></TR>
+  <TR><TD> llcp.miu </TD><TD> <b>128</b> (128 to 2176) </TD></TR>
+  <TR><TD> llcp.wks </TD><TD>   <b>1</b> (0 to 15) </TD></TR>
+</table> 
+<p>(default values in bold)</p>
+
+
+<h1>NFC Permissions</h1>
+
+<p>
+To change the NFC service settings such as enabling the NFC targets
+discovery or activating the secure element, an application must declare the
+NFC_ADMIN permission.
+</p>
+<p>
+To perform NFC raw communication with a remote NFC target in
+Reader/Write Mode or Peer-to-Peer Mode, an application must declare the NFC_RAW
+permission.
+</p>
+<p>
+To receive NDEF message or Secure Element intents, an application must declare
+the NFC_NOTIFY permission.
+</p>
+<p>
+To receive the LLCP link intent and perform an LLCP communication with a remote NFC target, an application must
+declare the NFC_LLCP permission.
+</p>
+
+
+<h1>NFC Usage</h1>
+
+<p>
+The following code samples illustrate the APIs usage regarding the NFC service
+use cases.
+</p>
+
+<h2>Reader/Writer Mode NDEF message notification</h2>
+
+<p>
+This code sample illustrates the NDEF message notification through an Intent declared in the manifest and a receiver implemented in the application.
+</p>
+<p>Main involved classes/methods:</p>
+
+<p>Manifest Example:</p>
+<pre>
+	&lt;receiver android:name=".NfcReaderDemoReceiver">
+            &lt;intent-filter>
+               &lt;action android:name= "com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED"/>
+            &lt;/intent-filter>
+        &lt;/receiver>
+</pre>
+
+<p>Receiver Example:</p>
+<ul>
+  <li>{@link com.trustedlogic.trustednfc.android.NdefMessage}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_TAG_DISCOVERED_ACTION}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_MESSAGE_EXTRA}</li>
+</ul>
+<pre>
+public class NdefMessageReceiverSample extends BroadcastReceiver {
+	public void onReceive(Context context, Intent intent) {
+		if (intent.getAction().equals(NfcManager.NDEF_TAG_DISCOVERERD_ACTION)) {
+			NdefMessage msg = intent.getParcelableExtra(NfcManager.NDEF_MESSAGE_EXTRA);
+		
+		/* Manage the NdefMessage received */
+	}
+</pre>
+
+<h2>Reader/Writer Mode raw exchange</h2>
+
+<p>
+This code sample illustrates raw exchanges with a NFC target in Reader/Writer
+mode.
+</p>
+<p>Main involved classes/methods:</p>
+<ul>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#openTagConnection}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcTag}</li>
+</ul>
+
+<pre>
+public class TagReaderSample {
+
+	/** The NFC manager to access NFC features */
+	private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
+
+	private void runTagReader() {
+		NfcTag tag = null;
+		String type;
+		byte[] cmd = { 0x01, 0x02, 0x03 };
+		byte[] res;
+
+		while (true) {
+			try {
+				Log.i("NFC example", "Please wave in front of the tag");
+				// Open a connection on next available tag
+				try {
+					tag = manager.openTagConnection();
+				} catch (NfcException e) {
+					// TODO: Handle open failure
+				}
+
+				// Look for a mifare 4k
+				type = tag.getType();
+				if (type.equals("Mifare4K")) {
+					Log.i("NFC example", "Tag detected");
+					tag.connect();
+					// Ready to communicate, we can send transceive !
+					res = tag.transceive(cmd);
+				} else {
+					Log.i("NFC example", "Unknown tag");
+				}
+			} catch (IOException e) {
+				// TODO: Handle broken connection
+			} finally {
+				if (tag != null) {
+					tag.close();
+				}
+			}
+		}
+	}
+}
+</pre>
+
+<h2>Peer-to-Peer Mode raw exchange</h2>
+
+<p>
+This code sample illustrates raw exchanges with a NFC target in Peer-to-Peer
+mode.
+</p>
+<p>Main involved classes/methods:</p>
+<ul>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#openP2pConnection}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.P2pDevice}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.P2pInitiator}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.P2pTarget}</li>
+</ul>
+
+<pre>
+public class P2pSample {
+
+	/** The NFC manager to access NFC features */
+	private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
+
+	private void runP2p() {
+		P2pDevice deviceP2p;
+		P2pInitiator initiator;
+		P2pTarget target;
+		byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		byte[] echo = new byte[data.length * 10];
+
+		try {
+			deviceP2p = manager.openP2pConnection();
+
+			if (deviceP2p.getMode() == P2pDevice.MODE_P2P_INITIATOR) {
+				target = new P2pTarget(deviceP2p);
+				// Connect to the detected P2P target
+				target.connect();
+				// send data to the target
+				target.transceive(data);
+				// disconnect the connected target
+				target.disconnect();
+			} else if (deviceP2p.getMode() == P2pDevice.MODE_P2P_TARGET) {
+				initiator = new P2pInitiator(deviceP2p);
+				//target in receive state 
+				echo = initiator.receive();	
+				// send back the data received
+				initiator.send(echo);
+			}
+		} catch (IOException e0) {
+
+		} catch (NfcException e1) {
+
+		}
+	}
+}
+</pre>
+
+<h2>Peer-to-Peer Mode LLCP exchange</h2>
+
+<p>
+This code sample illustrates how to get LLCP link state notification with the declaration of a Receiver in the manifest of the application  and the implementation 
+of the receiver in the application.
+</p>
+<p>Manifest Example:</p>
+<pre>
+	&lt;receiver android:name=".LlcpModeReceiverSample">
+            &lt;intent-filter>
+		&lt;action android:name= "com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED"/>
+            &lt;/intent-filter>
+        &lt;/receiver>
+</pre>
+
+<p>Receiver Example:</p>
+<ul>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_ACTION}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_EXTRA}</li> 
+</ul> 
+<pre>
+public class LlcpModeReceiverSample extends BroadcastReceiver {
+	public void onReceive(Context context, Intent intent) {
+
+		if (intent.getAction().equals(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION)){
+			byte[] aid = intent.getByteArrayExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA);
+			/* Create an LLCP service or client and start an LLCP communication */
+		} 
+	}
+</pre>
+
+
+<p>
+This code samples illustrate LLCP exchanges with a NFC Peer.
+</p>
+<p>Main involved classes/methods:</p>
+<ul>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpSocket}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpConnectionlessSocket}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpServiceSocket}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.LlcpSocket}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.LlcpConnectionlessSocket}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.LlcpPacket}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.LlcpServiceSocket}</li>
+</ul>
+
+<pre>
+public class LlcpServerSample {
+
+	/** The NFC manager to access NFC features */
+	private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
+
+	private void runLlcpClient() {
+		LlcpSocket sock;
+		byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		byte[] echo = new byte[data.length * 10];
+		int length = 0;
+
+		sock = manager.createLlcpSocket((short) 128, (byte) 2, 1024);
+		
+		// set a timeout in ms for connect request
+		sock.setConnectTimeout(10);
+		
+		try {
+			// Connect to remote service
+			// NOTE: could be sock.connect("com.trusted-logic.tnfc.testapp");
+			sock.connect((byte) 0x10);
+
+			// Send data
+			for (int i = 0; i < 10; i++) {
+				sock.send(data);
+			}
+
+			// Receive echo
+			while (length < 10 * data.length) {
+				length += sock.receive(echo);
+			}
+
+		} catch (IOException e) {
+			// TODO: Handle broken connection broken (link down, remote closure
+			// or connect rejected) or Timeout expired
+		}
+	}
+}
+</pre>
+
+<pre>
+public class LlcpClientSample {
+
+	/** The NFC manager to access NFC features */
+	private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
+
+	private void runLlcpClient() {
+		LlcpSocket sock;
+		byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		byte[] echo = new byte[data.length * 10];
+		int length = 0;
+
+		sock = manager.createLlcpSocket((short) 128, (byte) 2, 1024);
+		try {
+			// Connect to remote service
+			// NOTE: could be sock.connect("com.trusted-logic.tnfc.testapp");
+			sock.connect((byte) 0x10);
+
+			// Send data
+			for (int i = 0; i < 10; i++) {
+				sock.send(data);
+			}
+
+			// Receive echo
+			while (length < 10 * data.length) {
+				length += sock.receive(echo);
+			}
+
+		} catch (IOException e) {
+			// TODO: Handle broken connection broken (link down, remote closure
+			// or connect rejected)
+		}
+	}
+}
+</pre>
+
+<h2>Card Emulation Mode transaction notification</h2>
+
+<p>
+This code sample illustrates how to get the card emulation notification with the declaration of a Receiver in the manifest of the application  and the implementation 
+of the receiver in the application.
+</p>
+<p>Manifest Example:</p>
+<pre>
+	&lt;receiver android:name=".NfcReaderDemoReceiver">
+            &lt;intent-filter>
+		&lt;action android:name= "com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED"/>
+            &lt;/intent-filter>
+        &lt;/receiver>
+</pre>
+
+<p>Receiver Example:</p>
+<ul>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#TRANSACTION_DETECTED_ACTION}</li>
+  <li>{@link com.trustedlogic.trustednfc.android.NfcManager#AID_EXTRA}</li> 
+</ul> 
+<pre>
+public class CardEmulationReceiverSample extends BroadcastReceiver {
+	public void onReceive(Context context, Intent intent) {
+
+		if (intent.getAction().equals(NfcManager.TRANSACTION_DETECTED_ACTION)){
+			byte[] aid = intent.getByteArrayExtra(NfcManager.AID_EXTRA);
+			/* Manage the AID: */
+			/* For example start an activity related to this AID value or display a popup with the AID */
+		} 
+	}
+</pre>
+
+
+
+<h1>Multiple Applications rules</h1>
+
+<p>
+Several LLCP sockets can be created by a single application or by multiple
+applications by calling {@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpSocket}, 
+{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpConnectionlessSocket} or 
+{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpServiceSocket}, provided the local SAP
+numbers are differents.
+</p>
+
+<p>
+Only one application can open a raw connection by calling 
+{@link com.trustedlogic.trustednfc.android.NfcManager#openTagConnection} or
+{@link com.trustedlogic.trustednfc.android.NfcManager#openP2pConnection}.
+While this application has not closed or cancelled its connection, any other
+application that attempts to open another raw connection will raise an
+exception.
+During an open connnection, the card emulation mode is always enabled and 
+applications are able to receive card emulation intents.
+</p>
+
+<p>
+When an application opens a tag connection by calling 
+{@link com.trustedlogic.trustednfc.android.NfcManager#openTagConnection}, this operation is exclusive, no NDEF message intent are
+broadcast while the connection is not closed or canceled.
+</p>
+
+<p>
+When an application opens a peer-to-peer connection by calling
+{@link com.trustedlogic.trustednfc.android.NfcManager#openP2pConnection}, this operation is exclusive, no LLCP intent are broadcast and LLCP sockets are
+disabled while the connection is not closed or canceled. 
+</p>
+
+
+<h1>NFC Tag types</h1>
+
+<p>
+The {@link com.trustedlogic.trustednfc.android.NfcTag} type returned by
+{@link com.trustedlogic.trustednfc.android.NfcTag#getType} indicates the set of
+commands supported by the tag. These commands can be used in
+{@link com.trustedlogic.trustednfc.android.NfcTag#transceive}.
+</p>
+
+<TABLE BORDER="1">
+  <TR><TH> Tag Type </TH><TH> Returned string </TH></TR>
+  <TR><TD> Jewel/Topaz </TD><TD> Jewel </TD></TR>
+  <TR><TD> Mifare UltraLight </TD><TD> MifareUL </TD></TR>
+  <TR><TD> Mifare Standard 1K </TD><TD> Mifare1K </TD></TR>
+  <TR><TD> Mifare Standard 4K </TD><TD> Mifare4K </TD></TR>
+  <TR><TD> Mifare DESFIRE </TD><TD> MifareDESFIRE </TD></TR>
+  <TR><TD> Felica </TD><TD> Felica </TD></TR>
+  <TR><TD> ISO14443-4 A or B </TD><TD> Iso14443 </TD></TR>
+  <TR><TD> ISO15693 </TD><TD> Iso15693 </TD></TR>
+</TABLE> 
+
+</body>
+</html>
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index fffd185..64a2331 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -202,6 +202,14 @@
 	LOCAL_SHARED_LIBRARIES += libhwui
 endif
 
+ifeq ($(BOARD_HAVE_NFC),true)
+LOCAL_SHARED_LIBRARIES += \
+	libnfc_jni \
+	libnfc
+
+LOCAL_CFLAGS += -DHAVE_NFC
+endif
+
 ifeq ($(BOARD_HAVE_BLUETOOTH),true)
 LOCAL_C_INCLUDES += \
 	external/dbus \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ff62e0d..a4a1a70 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -170,6 +170,18 @@
 extern int register_android_content_res_ObbScanner(JNIEnv* env);
 extern int register_android_content_res_Configuration(JNIEnv* env);
 
+#ifdef HAVE_NFC
+extern int register_com_trustedlogic_trustednfc_android_internal_NativeNfcManager(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_internal_NativeNfcTag(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_internal_NativeNdefTag(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_NdefMessage(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_NdefRecord(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_internal_NativeP2pDevice(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_internal_NativeLlcpSocket(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_internal_NativeLlcpConnectionlessSocket(JNIEnv *env);
+extern int register_com_trustedlogic_trustednfc_android_internal_NativeLlcpServiceSocket(JNIEnv *env);
+#endif
+
 static AndroidRuntime* gCurRuntime = NULL;
 
 static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
@@ -1287,6 +1299,18 @@
 
     REG_JNI(register_android_content_res_ObbScanner),
     REG_JNI(register_android_content_res_Configuration),
+
+#ifdef HAVE_NFC
+    REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeNfcManager),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeNfcTag),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeNdefTag),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_NdefMessage),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_NdefRecord),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeP2pDevice),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeLlcpSocket),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeLlcpConnectionlessSocket),
+    REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeLlcpServiceSocket),
+#endif
 };
 
 /*
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 62c89fc..2a9eacf 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -34,7 +34,17 @@
     jfieldID flags;
 } gObbInfoClassInfo;
 
-static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
+static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
+{
+    jclass npeClazz;
+
+    npeClazz = env->FindClass(exc);
+    LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
+
+    env->ThrowNew(npeClazz, msg);
+}
+
+static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
         jobject obbInfo)
 {
     const char* filePath = env->GetStringUTFChars(file, JNI_FALSE);
@@ -42,7 +52,8 @@
     sp<ObbFile> obb = new ObbFile();
     if (!obb->readFrom(filePath)) {
         env->ReleaseStringUTFChars(file, filePath);
-        return JNI_FALSE;
+        doThrow(env, "java/io/IOException", "Could not read OBB file");
+        return;
     }
 
     env->ReleaseStringUTFChars(file, filePath);
@@ -51,13 +62,13 @@
 
     jstring packageName = env->NewStringUTF(packageNameStr);
     if (packageName == NULL) {
-        return JNI_FALSE;
+        doThrow(env, "java/io/IOException", "Could not read OBB file");
+        return;
     }
 
     env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName);
     env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion());
-
-    return JNI_TRUE;
+    env->SetIntField(obbInfo, gObbInfoClassInfo.flags, obb->getFlags());
 }
 
 /*
@@ -65,7 +76,7 @@
  */
 static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+    { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)V",
             (void*) android_content_res_ObbScanner_getObbInfo },
 };
 
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index d930065..05e1ff3 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -91,7 +91,7 @@
     LOGV("Registering sqlite logging func \n");
     int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path, 0));
     if (err != SQLITE_OK) {
-        LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err);
+        LOGW("sqlite returned error = %d when trying to register logging func.\n", err);
         return;
     }
     loggingFuncSet = true;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d5065f6..3a1d76f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -88,6 +88,10 @@
     <protected-broadcast android:name="android.hardware.action.USB_CAMERA_ATTACHED" />
     <protected-broadcast android:name="android.hardware.action.USB_CAMERA_DETACHED" />
 
+    <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED" />
+    <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED" />
+    <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED" />
+
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
@@ -338,6 +342,30 @@
         android:description="@string/permdesc_bluetooth"
         android:label="@string/permlab_bluetooth" />
 
+    <!-- Allows applications to access remote NFC devices
+         @hide -->
+    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_RAW"
+        android:permissionGroup="android.permission-group.NETWORK"
+        android:protectionLevel="dangerous"
+        android:description="@string/permdesc_nfcRaw"
+        android:label="@string/permlab_nfcRaw" />
+
+    <!-- Allows applications to be notified of remote NFC devices
+         @hide -->
+    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_NOTIFY"
+        android:permissionGroup="android.permission-group.NETWORK"
+        android:protectionLevel="dangerous"
+        android:description="@string/permdesc_nfcNotify"
+        android:label="@string/permlab_nfcNotify" />
+
+    <!-- Allows applications to be notified of remote NFC LLCP devices
+         @hide -->
+    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_LLCP"
+        android:permissionGroup="android.permission-group.NETWORK"
+        android:protectionLevel="dangerous"
+        android:description="@string/permdesc_nfcLlcp"
+        android:label="@string/permlab_nfcLlcp" />
+
     <!-- Allows applications to call into AccountAuthenticators. Only
     the system can get this permission. -->
     <permission android:name="android.permission.ACCOUNT_MANAGER"
@@ -848,6 +876,14 @@
         android:description="@string/permdesc_bluetoothAdmin"
         android:label="@string/permlab_bluetoothAdmin" />
 
+    <!-- Allows applications to change NFC connectivity settings 
+         @hide -->
+    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_ADMIN"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:description="@string/permdesc_nfcAdmin"
+        android:label="@string/permlab_nfcAdmin" />
+
     <!-- Allows an application to clear the caches of all installed
          applications on the device.  -->
     <permission android:name="android.permission.CLEAR_APP_CACHE"
diff --git a/core/res/res/color/primary_text_disable_only_holo_dark.xml b/core/res/res/color/primary_text_disable_only_holo_dark.xml
new file mode 100644
index 0000000..6de4583
--- /dev/null
+++ b/core/res/res/color/primary_text_disable_only_holo_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_dark_disabled"/>
+    <item android:color="@android:color/bright_foreground_dark"/>
+</selector>
+
diff --git a/core/res/res/color/primary_text_disable_only_holo_light.xml b/core/res/res/color/primary_text_disable_only_holo_light.xml
new file mode 100644
index 0000000..87a92c8
--- /dev/null
+++ b/core/res/res/color/primary_text_disable_only_holo_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_light_disabled"/>
+    <item android:color="@android:color/bright_foreground_light"/>
+</selector>
+
diff --git a/core/res/res/color/primary_text_focused_holo_dark.xml b/core/res/res/color/primary_text_focused_holo_dark.xml
new file mode 100644
index 0000000..80c68a4d
--- /dev/null
+++ b/core/res/res/color/primary_text_focused_holo_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:state_focused="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:state_pressed="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:color="@android:color/bright_foreground_dark" />
+</selector>
+
diff --git a/core/res/res/color/primary_text_holo_dark.xml b/core/res/res/color/primary_text_holo_dark.xml
new file mode 100644
index 0000000..69ee309
--- /dev/null
+++ b/core/res/res/color/primary_text_holo_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_dark_disabled"/>
+    <item android:state_window_focused="false" android:color="@android:color/bright_foreground_dark"/>
+    <item android:state_pressed="true" android:color="@android:color/bright_foreground_dark"/>
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_dark"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark"/>
+    <item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/primary_text_holo_light.xml b/core/res/res/color/primary_text_holo_light.xml
new file mode 100644
index 0000000..a8d31ce
--- /dev/null
+++ b/core/res/res/color/primary_text_holo_light.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_light_disabled"/>
+    <item android:state_window_focused="false" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_pressed="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
+    
+</selector>
+
diff --git a/core/res/res/color/primary_text_nodisable_holo_dark.xml b/core/res/res/color/primary_text_nodisable_holo_dark.xml
new file mode 100644
index 0000000..f45088f
--- /dev/null
+++ b/core/res/res/color/primary_text_nodisable_holo_dark.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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/primary_text_nodisable_holo_light.xml b/core/res/res/color/primary_text_nodisable_holo_light.xml
new file mode 100644
index 0000000..331e8c3
--- /dev/null
+++ b/core/res/res/color/primary_text_nodisable_holo_light.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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/search_url_text_holo.xml b/core/res/res/color/search_url_text_holo.xml
new file mode 100644
index 0000000..78093ba
--- /dev/null
+++ b/core/res/res/color/search_url_text_holo.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@android:color/search_url_text_pressed"/>
+    <item android:state_selected="true" android:color="@android:color/search_url_text_selected"/>
+    <item android:color="@android:color/search_url_text_normal"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_holo_dark.xml b/core/res/res/color/secondary_text_holo_dark.xml
new file mode 100644
index 0000000..376156e
--- /dev/null
+++ b/core/res/res/color/secondary_text_holo_dark.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
+    <item android:state_window_focused="false" android:color="@android:color/dim_foreground_dark"/>
+    <item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
+    <item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:state_pressed="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
+    <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_holo_light.xml b/core/res/res/color/secondary_text_holo_light.xml
new file mode 100644
index 0000000..b791aeb
--- /dev/null
+++ b/core/res/res/color/secondary_text_holo_light.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
+    <item android:state_window_focused="false" android:color="@android:color/dim_foreground_light"/>
+    <!-- Since there is only one selector (for both light and dark), the light's selected state shouldn't be inversed like the dark's. -->
+    <item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
+    <item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
+    <item android:state_pressed="true" android:color="@android:color/dim_foreground_light"/>
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
+    <item android:color="@android:color/dim_foreground_light"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_nodisable_holo_dark.xml b/core/res/res/color/secondary_text_nodisable_holo_dark.xml
new file mode 100644
index 0000000..8c22241
--- /dev/null
+++ b/core/res/res/color/secondary_text_nodisable_holo_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_nodisable_holo_light.xml b/core/res/res/color/secondary_text_nodisable_holo_light.xml
new file mode 100644
index 0000000..8c22241
--- /dev/null
+++ b/core/res/res/color/secondary_text_nodisable_holo_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/tertiary_text_holo_dark.xml b/core/res/res/color/tertiary_text_holo_dark.xml
new file mode 100644
index 0000000..269ff71
--- /dev/null
+++ b/core/res/res/color/tertiary_text_holo_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="#808080"/>
+    <item android:state_window_focused="false" android:color="#808080"/>
+    <item android:state_pressed="true" android:color="#808080"/>
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_light"/>
+    <item android:color="#808080"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/tertiary_text_holo_light.xml b/core/res/res/color/tertiary_text_holo_light.xml
new file mode 100644
index 0000000..9e5c2d9
--- /dev/null
+++ b/core/res/res/color/tertiary_text_holo_light.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="#808080"/>
+    <item android:state_window_focused="false" android:color="#808080"/>
+    <item android:state_pressed="true" android:color="#808080"/>
+    <item android:state_selected="true" android:color="#808080"/>
+    <item android:color="#808080"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/widget_edittext_holo_dark.xml b/core/res/res/color/widget_edittext_holo_dark.xml
new file mode 100644
index 0000000..4f3eb62
--- /dev/null
+++ b/core/res/res/color/widget_edittext_holo_dark.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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="false" android:color="@android:color/bright_foreground_light"/> <!-- unfocused -->
+    <item android:color="@android:color/bright_foreground_light"/>
+</selector>
diff --git a/core/res/res/color/widget_edittext_holo_light.xml b/core/res/res/color/widget_edittext_holo_light.xml
new file mode 100644
index 0000000..7b950d4
--- /dev/null
+++ b/core/res/res/color/widget_edittext_holo_light.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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="false" android:color="@android:color/bright_foreground_dark"/> <!-- unfocused -->
+    <item android:color="@android:color/bright_foreground_dark"/>
+</selector>
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png
index d72e2b9..d93e580 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png
index 240a044..ffbe776 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png
index d72e2b9..d93e580 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png
index 240a044..ffbe776 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png
index 911e1aa..4148ac4 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_holo_light.png
index 4ca3c56..2cc946f 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png
index 08f41812..9f9cb01 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png
index d3754dd..56425d2 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png
index 264f102..f819928 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png
index 48506bf..57cb512 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
index 7805458..1f7aeee 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png
index 4e268d5..1f740ad 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png
index 7805458..1f7aeee 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png
index 4e268d5..1f740ad 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png
index 5541c67..d0c4415 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_holo_light.png
index 768c4af..af84d4b 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
index 37e3953..91e5f14 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
index fc29e46..0cf7ed2 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png
index a0beac4..e758aab 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png
index 5df45c7..2edf656 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..3deb385
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..de378a5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
new file mode 100644
index 0000000..35f8b3d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
new file mode 100644
index 0000000..3f45375
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
new file mode 100644
index 0000000..ea58bf7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
new file mode 100644
index 0000000..b225aaff
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
new file mode 100644
index 0000000..b5b1533
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
new file mode 100644
index 0000000..81b8a75
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
new file mode 100644
index 0000000..eb8d85a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
new file mode 100644
index 0000000..6777ebfa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_dark.png
new file mode 100644
index 0000000..2a7505b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_light.png
new file mode 100644
index 0000000..bbb01f0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_dark.png
new file mode 100644
index 0000000..b617a2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_light.png
new file mode 100644
index 0000000..fd59f4a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_dark.png
new file mode 100644
index 0000000..5d17cde
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_light.png
new file mode 100644
index 0000000..b6b4bf1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_dark.png
new file mode 100644
index 0000000..eab5039
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_light.png
new file mode 100644
index 0000000..b6b4bf1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_dark.png
new file mode 100644
index 0000000..edf2296
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_light.png
new file mode 100644
index 0000000..68afa4c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_dark.png
new file mode 100644
index 0000000..c7df168
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_light.png
new file mode 100644
index 0000000..5a9087b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png
index 301c97d..dd18b7a 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png
index 657c8e5..66d538f 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png
index 5e6ef2b..4e777f8 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png
index 342bf11..6062033 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png
index d11ae85..683a883 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png
index 68bd1df..19524ff 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png
index 5b0dbe8..2e1111b 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png
index 45ae36b..90639ec 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png
index c3a0d48..1907215 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png
index ca22358..b51c7ad 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png
index 6c05f47..06d39cc 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png
index a17fa1e..06a4314 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_dark.png
new file mode 100644
index 0000000..500490d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_light.png
new file mode 100644
index 0000000..f6690c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_dark.png
new file mode 100644
index 0000000..933d2fe
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_light.png
new file mode 100644
index 0000000..c07445a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..128a8dd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..da05c23
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
new file mode 100755
index 0000000..da66a98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
new file mode 100755
index 0000000..3ac8417
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
new file mode 100755
index 0000000..fc9d493
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
new file mode 100755
index 0000000..315ae3b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_holo.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_holo.9.png
new file mode 100755
index 0000000..f903bdb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
new file mode 100755
index 0000000..ee9590c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
new file mode 100755
index 0000000..cbc9da2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
new file mode 100755
index 0000000..1792063
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
new file mode 100755
index 0000000..097025b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..5b92d7c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..a244f45
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
new file mode 100755
index 0000000..9218f91
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
new file mode 100755
index 0000000..81802f8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
new file mode 100755
index 0000000..9dea983
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
new file mode 100755
index 0000000..fc2374b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png
new file mode 100755
index 0000000..4c1d89d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
new file mode 100755
index 0000000..18bb6bd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
new file mode 100755
index 0000000..de78a9f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
new file mode 100755
index 0000000..2269326
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
new file mode 100755
index 0000000..ead4ccf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_holo_dark.png b/core/res/res/drawable-hdpi/cab_divider_holo_dark.png
new file mode 100755
index 0000000..e6f61fc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_holo_light.png b/core/res/res/drawable-hdpi/cab_divider_holo_light.png
new file mode 100755
index 0000000..2f97a29
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_vertical_dark.png b/core/res/res/drawable-hdpi/cab_divider_vertical_dark.png
new file mode 100755
index 0000000..b1f035c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_vertical_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_vertical_light.png b/core/res/res/drawable-hdpi/cab_divider_vertical_light.png
new file mode 100755
index 0000000..2183b12
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_vertical_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_holo_dark.9.png
new file mode 100755
index 0000000..662d63c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_holo_light.9.png b/core/res/res/drawable-hdpi/cab_holo_light.9.png
new file mode 100755
index 0000000..e8cbde1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_ic_close_focused_holo.png b/core/res/res/drawable-hdpi/cab_ic_close_focused_holo.png
new file mode 100755
index 0000000..861e0a1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_ic_close_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_ic_close_normal_holo.png b/core/res/res/drawable-hdpi/cab_ic_close_normal_holo.png
new file mode 100755
index 0000000..036f362
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_ic_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_ic_close_pressed_holo.png b/core/res/res/drawable-hdpi/cab_ic_close_pressed_holo.png
new file mode 100755
index 0000000..be8c2ff
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_ic_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_dark.png
new file mode 100644
index 0000000..6de74a7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_light.png
new file mode 100644
index 0000000..a0e201d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_dark.png
new file mode 100644
index 0000000..9bb69c4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_light.png
new file mode 100644
index 0000000..bffc5aa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_dark.png
new file mode 100644
index 0000000..bbe04b6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_light.png
new file mode 100644
index 0000000..62f1efa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_dark.png
new file mode 100644
index 0000000..c4026a8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_light.png
new file mode 100644
index 0000000..409aa3e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_dark.png
new file mode 100644
index 0000000..10f1bc4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_light.png
new file mode 100644
index 0000000..20a1efa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_dark.png
new file mode 100644
index 0000000..0a10ec8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_light.png
new file mode 100644
index 0000000..34b53ee
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_dark.png
new file mode 100644
index 0000000..7f14620
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_light.png
new file mode 100644
index 0000000..cabf936
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_dark.png
new file mode 100644
index 0000000..bcddb31
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_light.png
new file mode 100644
index 0000000..84160e5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
new file mode 100644
index 0000000..3a84de9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo.9.png b/core/res/res/drawable-hdpi/dialog_full_holo.9.png
new file mode 100644
index 0000000..5d2e4e1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_full_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo.9.png
new file mode 100644
index 0000000..dc5e79d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo.9.png b/core/res/res/drawable-hdpi/dialog_top_holo.9.png
new file mode 100644
index 0000000..0275c18
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_top_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_holo_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..e8e1deb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_holo_light.9.png b/core/res/res/drawable-hdpi/divider_horizontal_holo_light.9.png
new file mode 100644
index 0000000..9e6cbbe
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_holo_dark.9.png b/core/res/res/drawable-hdpi/divider_vertical_holo_dark.9.png
new file mode 100644
index 0000000..deacb73
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_vertical_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_holo_light.9.png b/core/res/res/drawable-hdpi/divider_vertical_holo_light.9.png
new file mode 100644
index 0000000..cd2c826
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_vertical_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_close_normal_holo.png b/core/res/res/drawable-hdpi/ic_dialog_close_normal_holo.png
new file mode 100644
index 0000000..5ee5bb8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_close_pressed_holo.png b/core/res/res/drawable-hdpi/ic_dialog_close_pressed_holo.png
new file mode 100644
index 0000000..792db06
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_focused_holo.png b/core/res/res/drawable-hdpi/ic_dialog_focused_holo.png
new file mode 100644
index 0000000..e208575
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_arrow_left_holo_dark.png b/core/res/res/drawable-hdpi/tab_arrow_left_holo_dark.png
new file mode 100644
index 0000000..cfd6f78
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_arrow_left_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_arrow_left_holo_light.png b/core/res/res/drawable-hdpi/tab_arrow_left_holo_light.png
new file mode 100644
index 0000000..036aa8c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_arrow_left_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_arrow_right_holo_dark.png b/core/res/res/drawable-hdpi/tab_arrow_right_holo_dark.png
new file mode 100644
index 0000000..b226038
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_arrow_right_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_arrow_right_holo_light.png b/core/res/res/drawable-hdpi/tab_arrow_right_holo_light.png
new file mode 100644
index 0000000..0e5fbe6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_arrow_right_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_divider_holo_dark.png b/core/res/res/drawable-hdpi/tab_divider_holo_dark.png
new file mode 100644
index 0000000..112cb04
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_divider_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_divider_holo_light.png b/core/res/res/drawable-hdpi/tab_divider_holo_light.png
new file mode 100644
index 0000000..1bf4d38
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_divider_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selector_holo_dark.9.png b/core/res/res/drawable-hdpi/tab_selector_holo_dark.9.png
new file mode 100644
index 0000000..f01b9bc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selector_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_strip_holo.9.png b/core/res/res/drawable-hdpi/tab_strip_holo.9.png
new file mode 100644
index 0000000..d937f6b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_strip_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
index c67d04d..7ec2192 100644
--- a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
index 1292ebf..c03e4f6 100644
--- a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
index a39982a..6642717 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
index 1f224b9..9572752 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png
index 66f18cc..0ad248c 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png
index 2664384..b7a07c4 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png
index bbf53c5..a271ac9b 100644
--- a/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png
index ce49b8d..521722d 100644
--- a/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_search_default_holo_dark.9.png
new file mode 100644
index 0000000..5b62564
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_search_default_holo_light.9.png
new file mode 100644
index 0000000..881edeb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_search_selected_holo_dark.9.png
new file mode 100644
index 0000000..cb3f35b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_search_selected_holo_light.9.png
new file mode 100644
index 0000000..742b137
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png
index bbf53c5..a271ac9b 100644
--- a/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png
index ce49b8d..521722d 100644
--- a/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png
index a603fb1..3fac4aa 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png
index 69e9ff9..3da9a46 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png
index a603fb1..3fac4aa 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png
index 69e9ff9..3da9a46 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png
index 5e44c29..b03f356 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_holo_light.png
index 5b2ec92..9dbbd49 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png
index 611bb1d..0dcb9de 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png
index 5a0ea441..b25fdc4 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png
index aa28df2..89ea3a8 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png
index ade1136..3fa45d9 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
index f19972a..48cc017 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png
index 13ef46e..c9ebbca 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png
index f19972a..48cc017 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png
index 13ef46e..c9ebbca 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png
index 130d562..ca4d509 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_holo_light.png
index 6b7808b..158f3a1 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
index df753f5..0722cda 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
index 6a4dd2c..4de166e 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png
index 7586881..5b93f8b 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png
index 24701ce..74ab250 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..9bc1ee8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..cc643ea
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
new file mode 100644
index 0000000..0586d52
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
new file mode 100644
index 0000000..dd6c1a0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
new file mode 100644
index 0000000..e8f07cb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
new file mode 100644
index 0000000..0685f1e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
new file mode 100644
index 0000000..6b33fa4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
new file mode 100644
index 0000000..eb728ed
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
new file mode 100644
index 0000000..4a15d9d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
new file mode 100644
index 0000000..6718ff7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_dark.png
new file mode 100644
index 0000000..f21142e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_light.png
new file mode 100644
index 0000000..a1031fc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_dark.png
new file mode 100644
index 0000000..61243c5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_light.png
new file mode 100644
index 0000000..faa55e0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_dark.png
new file mode 100644
index 0000000..0c645da
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_light.png
new file mode 100644
index 0000000..5efc321
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_dark.png
new file mode 100644
index 0000000..96bcdc5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_light.png
new file mode 100644
index 0000000..5efc321
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_dark.png
new file mode 100644
index 0000000..96413ef
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_light.png
new file mode 100644
index 0000000..1cb5432
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_dark.png
new file mode 100644
index 0000000..2e8404a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_light.png
new file mode 100644
index 0000000..b3e14b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png
index 16c1c6b..a3cef04 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png
index e8287f3..e8def55 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png
index b25217b..1a9310b 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png
index b63b9b0..bc28b5b 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png
index bef7572..2b0ddd1 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png
index af754e1..745cf19 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png
index 4ed7471..9954500 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png
index 62aaa41..fa67a43 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png
index 7cf91c6..c15c310 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png
index 0d93507..aa07c5a 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png
index 56f6f5b..e96a74f 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png
index 48dd8e9..c51c96c 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_dark.png
new file mode 100644
index 0000000..5f74c70
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_light.png
new file mode 100644
index 0000000..408e50e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_dark.png
new file mode 100644
index 0000000..ff60bc2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_light.png
new file mode 100644
index 0000000..2125c24
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..7d1e16d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..92e86cd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
new file mode 100755
index 0000000..1cf473b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
new file mode 100755
index 0000000..d6f2125
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
new file mode 100755
index 0000000..31f7f8c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
new file mode 100755
index 0000000..82425d5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_holo.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_holo.9.png
new file mode 100755
index 0000000..0ca659e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
new file mode 100755
index 0000000..16f19fc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
new file mode 100755
index 0000000..e2c7702
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
new file mode 100755
index 0000000..d61470c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
new file mode 100755
index 0000000..4019fee
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..ba354e3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..9391b2e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
new file mode 100755
index 0000000..601ff2c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
new file mode 100755
index 0000000..90c259a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
new file mode 100755
index 0000000..857c757
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
new file mode 100755
index 0000000..ef16a48
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png
new file mode 100755
index 0000000..66cbe48
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
new file mode 100755
index 0000000..d47ec8f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
new file mode 100755
index 0000000..2951caf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
new file mode 100755
index 0000000..141b4dd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
new file mode 100755
index 0000000..913aed5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_holo_dark.png b/core/res/res/drawable-mdpi/cab_divider_holo_dark.png
new file mode 100755
index 0000000..57cc8a4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_holo_light.png b/core/res/res/drawable-mdpi/cab_divider_holo_light.png
new file mode 100755
index 0000000..ec85701
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_vertical_dark.png b/core/res/res/drawable-mdpi/cab_divider_vertical_dark.png
new file mode 100755
index 0000000..f7ed6df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_vertical_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_vertical_light.png b/core/res/res/drawable-mdpi/cab_divider_vertical_light.png
new file mode 100755
index 0000000..73ac0d9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_vertical_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_holo_dark.9.png
new file mode 100755
index 0000000..6c85300
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_holo_light.9.png b/core/res/res/drawable-mdpi/cab_holo_light.9.png
new file mode 100755
index 0000000..c82352a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_ic_close_focused_holo.png b/core/res/res/drawable-mdpi/cab_ic_close_focused_holo.png
new file mode 100755
index 0000000..df170c4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_ic_close_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_ic_close_normal_holo.png b/core/res/res/drawable-mdpi/cab_ic_close_normal_holo.png
new file mode 100755
index 0000000..9482ce7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_ic_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_ic_close_pressed_holo.png b/core/res/res/drawable-mdpi/cab_ic_close_pressed_holo.png
new file mode 100755
index 0000000..d115d20
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_ic_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_dark.png
new file mode 100644
index 0000000..e1094be
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_light.png
new file mode 100644
index 0000000..c17377c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_dark.png
new file mode 100644
index 0000000..f2c5290
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_light.png
new file mode 100644
index 0000000..06bd903
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_dark.png
new file mode 100644
index 0000000..be624c2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_light.png
new file mode 100644
index 0000000..2493ce2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_dark.png
new file mode 100644
index 0000000..7cdc1df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_light.png
new file mode 100644
index 0000000..f977e72
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_dark.png
new file mode 100644
index 0000000..f824f76
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_light.png
new file mode 100644
index 0000000..a76f68c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_dark.png
new file mode 100644
index 0000000..e4fd418
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_light.png
new file mode 100644
index 0000000..d572ef5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_dark.png
new file mode 100644
index 0000000..686707e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_light.png
new file mode 100644
index 0000000..17dd1da
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_dark.png
new file mode 100644
index 0000000..8cf2b1b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_light.png
new file mode 100644
index 0000000..c2df6da
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
new file mode 100644
index 0000000..cb3d0f2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo.9.png b/core/res/res/drawable-mdpi/dialog_full_holo.9.png
new file mode 100644
index 0000000..0ec9421
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_full_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo.9.png
new file mode 100644
index 0000000..36da5ca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo.9.png b/core/res/res/drawable-mdpi/dialog_top_holo.9.png
new file mode 100644
index 0000000..1ed519b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_top_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_holo_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..d6548c6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_holo_light.9.png b/core/res/res/drawable-mdpi/divider_horizontal_holo_light.9.png
new file mode 100644
index 0000000..9a42dd2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_holo_dark.9.png b/core/res/res/drawable-mdpi/divider_vertical_holo_dark.9.png
new file mode 100644
index 0000000..4c6968c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_vertical_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_holo_light.9.png b/core/res/res/drawable-mdpi/divider_vertical_holo_light.9.png
new file mode 100644
index 0000000..7ddf1b6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_vertical_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_close_normal_holo.png b/core/res/res/drawable-mdpi/ic_dialog_close_normal_holo.png
new file mode 100644
index 0000000..7f2a029
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_dialog_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_close_pressed_holo.png b/core/res/res/drawable-mdpi/ic_dialog_close_pressed_holo.png
new file mode 100644
index 0000000..daa8e18
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_dialog_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_focused_holo.png b/core/res/res/drawable-mdpi/ic_dialog_focused_holo.png
new file mode 100644
index 0000000..179f2de
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_dialog_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_arrow_left_holo_dark.png b/core/res/res/drawable-mdpi/tab_arrow_left_holo_dark.png
new file mode 100644
index 0000000..4f8bafe
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_arrow_left_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_arrow_left_holo_light.png b/core/res/res/drawable-mdpi/tab_arrow_left_holo_light.png
new file mode 100644
index 0000000..8e225fc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_arrow_left_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_arrow_right_holo_dark.png b/core/res/res/drawable-mdpi/tab_arrow_right_holo_dark.png
new file mode 100644
index 0000000..0a8006d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_arrow_right_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_arrow_right_holo_light.png b/core/res/res/drawable-mdpi/tab_arrow_right_holo_light.png
new file mode 100644
index 0000000..85aae47
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_arrow_right_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_divider_holo_dark.png b/core/res/res/drawable-mdpi/tab_divider_holo_dark.png
new file mode 100644
index 0000000..89d7b8b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_divider_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_divider_holo_light.png b/core/res/res/drawable-mdpi/tab_divider_holo_light.png
new file mode 100644
index 0000000..878b2b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_divider_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selector_holo_dark.9.png b/core/res/res/drawable-mdpi/tab_selector_holo_dark.9.png
new file mode 100644
index 0000000..30d30df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selector_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
index a9a2cc6..3a5f36d 100644
--- a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
index 36003cf..b8cc76f 100644
--- a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
index cec82dd..a1f0c71 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
index 8a9b11f..71e3103 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png
index 545924c..ac6d406 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png
index 53ee26c..bb6e953 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png
index 0e7c24d..7667d95 100644
--- a/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png
index fee09a6..269affd 100644
--- a/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_search_default_holo_dark.9.png
new file mode 100644
index 0000000..3549948
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_search_default_holo_light.9.png
new file mode 100644
index 0000000..6db03cb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_search_selected_holo_dark.9.png
new file mode 100644
index 0000000..670439a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_search_selected_holo_light.9.png
new file mode 100644
index 0000000..90e26e2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png
index 0e7c24d..7667d95 100644
--- a/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png
index fee09a6..269affd 100644
--- a/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_holo_dark.xml b/core/res/res/drawable/btn_default_holo_dark.xml
new file mode 100644
index 0000000..c250070
--- /dev/null
+++ b/core/res/res/drawable/btn_default_holo_dark.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_normal_holo_dark" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_default_disabled_holo_dark" />
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_default_pressed_holo_dark" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_focused_holo_dark" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_default_normal_holo_dark" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_default_disabled_focused_holo_dark" />
+    <item
+         android:drawable="@drawable/btn_default_disabled_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/btn_toggle_holo_dark.xml b/core/res/res/drawable/btn_toggle_holo_dark.xml
new file mode 100644
index 0000000..658ca9e
--- /dev/null
+++ b/core/res/res/drawable/btn_toggle_holo_dark.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_dark" />
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_on_disabled_holo_dark" />
+    <item android:state_checked="true" android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_on_pressed_holo_dark" />
+    <item android:state_checked="true"
+        android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_focused_holo_dark" />
+    <item android:state_checked="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_dark" />
+    <item android:state_checked="true"  android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_on_disabled_focused_holo_dark" />
+    <item android:state_checked="true"
+         android:drawable="@drawable/btn_toggle_on_disabled_holo_dark" />
+
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_dark" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_off_disabled_holo_dark" />
+    <item android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_off_pressed_holo_dark" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_focused_holo_dark" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_dark" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_off_disabled_focused_holo_dark" />
+    <item
+         android:drawable="@drawable/btn_toggle_off_disabled_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/btn_toggle_holo_light.xml b/core/res/res/drawable/btn_toggle_holo_light.xml
new file mode 100644
index 0000000..d3f1012
--- /dev/null
+++ b/core/res/res/drawable/btn_toggle_holo_light.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_light" />
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_on_disabled_holo_light" />
+    <item android:state_checked="true" android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_on_pressed_holo_light" />
+    <item android:state_checked="true"
+        android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_focused_holo_light" />
+    <item android:state_checked="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_light" />
+    <item android:state_checked="true"  android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_on_disabled_focused_holo_light" />
+    <item android:state_checked="true"
+         android:drawable="@drawable/btn_toggle_on_disabled_holo_light" />
+
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_light" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_off_disabled_holo_light" />
+    <item android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_off_pressed_holo_light" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_focused_holo_light" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_light" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_off_disabled_focused_holo_light" />
+    <item
+         android:drawable="@drawable/btn_toggle_off_disabled_holo_light" />
+</selector>
diff --git a/core/res/res/drawable/cab_ic_close_holo.xml b/core/res/res/drawable/cab_ic_close_holo.xml
new file mode 100644
index 0000000..1baf7cc
--- /dev/null
+++ b/core/res/res/drawable/cab_ic_close_holo.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true"
+          android:drawable="@drawable/cab_ic_close_pressed_holo" />
+    <item android:state_focused="true"
+          android:drawable="@drawable/cab_ic_close_focused_holo" />
+    <item android:drawable="@drawable/cab_ic_close_normal_holo" />
+</selector>
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index c665f7a..94dcb6a 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -30,5 +30,5 @@
     android:layout_height="match_parent"
     android:layout_marginTop="5px"
     android:cacheColorHint="@null"
-    android:divider="@android:drawable/divider_horizontal_bright"
+    android:divider="?android:attr/listDividerAlertDialog"
     android:scrollbars="vertical" />
diff --git a/core/res/res/layout/select_dialog_item.xml b/core/res/res/layout/select_dialog_item.xml
index 30fe02e..96fdcc6 100644
--- a/core/res/res/layout/select_dialog_item.xml
+++ b/core/res/res/layout/select_dialog_item.xml
@@ -29,7 +29,7 @@
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:textAppearance="?android:attr/textAppearanceLarge"
-    android:textColor="@android:color/primary_text_light_disable_only"
+    android:textColor="?android:attr/textColorAlertDialogListItem"
     android:gravity="center_vertical"
     android:paddingLeft="14dip"
     android:paddingRight="15dip"
diff --git a/core/res/res/layout/select_dialog_multichoice.xml b/core/res/res/layout/select_dialog_multichoice.xml
index b646a4c..a9be014 100644
--- a/core/res/res/layout/select_dialog_multichoice.xml
+++ b/core/res/res/layout/select_dialog_multichoice.xml
@@ -20,7 +20,7 @@
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:textAppearance="?android:attr/textAppearanceLarge"
-    android:textColor="@android:color/primary_text_light_disable_only"
+    android:textColor="?android:attr/textColorAlertDialogListItem"
     android:gravity="center_vertical"
     android:paddingLeft="12dip"
     android:paddingRight="7dip"
diff --git a/core/res/res/layout/select_dialog_singlechoice.xml b/core/res/res/layout/select_dialog_singlechoice.xml
index c3c2073..1b9c973 100644
--- a/core/res/res/layout/select_dialog_singlechoice.xml
+++ b/core/res/res/layout/select_dialog_singlechoice.xml
@@ -20,7 +20,7 @@
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:textAppearance="?android:attr/textAppearanceLarge"
-    android:textColor="@android:color/primary_text_light_disable_only"
+    android:textColor="?android:attr/textColorAlertDialogListItem"
     android:gravity="center_vertical"
     android:paddingLeft="12dip"
     android:paddingRight="7dip"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 19b76ab..317b3f3 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -101,6 +101,9 @@
         <attr name="textColorHighlightInverse" format="reference|color" />
         <!-- Color of link text (URLs), when used in a light theme. @hide -->
         <attr name="textColorLinkInverse" format="reference|color" />
+
+        <!-- Color of list item text in alert dialogs. -->
+        <attr name="textColorAlertDialogListItem" format="reference|color" />
         
         <!-- Search widget more corpus result item background. -->
         <attr name="searchWidgetCorpusItemBackground" format="reference|color" />
@@ -197,6 +200,8 @@
         <!-- The list item height for search results. @hide -->
         <attr name="searchResultListItemHeight" format="dimension" />
         <attr name="listDivider" format="reference" />
+        <!-- The list divider used in alert dialogs. -->
+        <attr name="listDividerAlertDialog" format="reference" />
         <!-- TextView style for list separators. -->
         <attr name="listSeparatorTextViewStyle" format="reference" />
         <!-- The preferred left padding for an expandable list item (for child-specific layouts,
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index e9a4ca3..623368d 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -18,8 +18,8 @@
 */
 -->
 <resources>
-	<drawable name="screen_background_light">#ffefefef</drawable>
-	<drawable name="screen_background_dark">#ff101010</drawable>
+	  <drawable name="screen_background_light">#fff3f3f3</drawable>
+	  <drawable name="screen_background_dark">#ff000000</drawable>
     <drawable name="status_bar_closed_default_background">#ff000000</drawable>
     <drawable name="status_bar_opened_default_background">#ff000000</drawable>
     <drawable name="search_bar_default_color">#ff000000</drawable>
@@ -36,8 +36,8 @@
     <color name="white">#ffffffff</color>
     <color name="black">#ff000000</color>
     <color name="transparent">#00000000</color>
-    <color name="background_dark">#ff101010</color>
-    <color name="background_light">#ffefefef</color>
+    <color name="background_dark">#ff000000</color>
+    <color name="background_light">#fff3f3f3</color>
     <color name="bright_foreground_dark">@android:color/background_light</color>
     <color name="bright_foreground_light">@android:color/background_dark</color>
     <color name="bright_foreground_dark_disabled">#80ffffff</color>
@@ -103,5 +103,32 @@
     <color name="keyguard_text_color_soundon">#e69310</color>
     <color name="keyguard_text_color_decline">#fe0a5a</color>
 
+    <!-- For holo theme -->
+	  <drawable name="screen_background_holo_light">#fff3f3f3</drawable>
+	  <drawable name="screen_background_holo_dark">#ff000000</drawable>
+    <color name="background_holo_dark">#ff000000</color>
+    <color name="background_holo_light">#fff3f3f3</color>
+    <color name="bright_foreground_holo_dark">@android:color/background_holo_light</color>
+    <color name="bright_foreground_holo_light">@android:color/background_holo_dark</color>
+    <color name="bright_foreground_disabled_holo_dark">#80ffffff</color>
+    <color name="bright_foreground_disabled_holo_light">#80000000</color>
+    <color name="bright_foreground_inverse_holo_dark">@android:color/bright_foreground_holo_light</color>
+    <color name="bright_foreground_inverse_holo_light">@android:color/bright_foreground_holo_dark</color>
+    <color name="dim_foreground_holo_dark">#bebebe</color>
+    <color name="dim_foreground_disabled_holo_dark">#80bebebe</color>
+    <color name="dim_foreground_inverse_holo_dark">#323232</color>
+    <color name="dim_foreground_inverse_disabled_holo_dark">#80323232</color>
+    <color name="hint_foreground_holo_dark">#808080</color>
+    <color name="dim_foreground_holo_light">#323232</color>
+    <color name="dim_foreground_disabled_holo_light">#80323232</color>
+    <color name="dim_foreground_inverse_holo_light">#bebebe</color>
+    <color name="dim_foreground_inverse_disabled_holo_light">#80bebebe</color>
+    <color name="hint_foreground_holo_light">#808080</color>
+    <color name="highlight_background_holo">#cc475925</color>
+    <color name="highlight_background_inverse_holo">#ccd2e461</color>
+    <color name="highlighted_text_holo_dark">#cc475925</color>
+    <color name="highlighted_text_holo_light">#ccd2e461</color>
+    <color name="link_text_holo_dark">#5c5cff</color>
+    <color name="link_text_holo_light">#0000ee</color>
 </resources>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b4e1fcb..f704874 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -37,7 +37,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 cdeb899..adcbb10 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1358,6 +1358,8 @@
   <public type="attr" name="textAppearanceSmallPopupMenu" />
   <public type="attr" name="breadCrumbTitle" />
   <public type="attr" name="breadCrumbShortTitle" />
+  <public type="attr" name="listDividerAlertDialog" />
+  <public type="attr" name="textColorAlertDialogListItem" />
 
   <public type="anim" name="animator_fade_in" />
   <public type="anim" name="animator_fade_out" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 153dd29..7a9b59a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1157,6 +1157,30 @@
       connections with paired devices.</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_nfcAdmin">NFC administration</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_nfcAdmin">Allows an application to configure
+      the local NFC phone.</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_nfcRaw">NFC full access to remote device</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_nfcRaw">Allows an application to access
+      remote NFC devices.</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_nfcNotify">NFC notification from remote device</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_nfcNotify">Allows an application to be notified
+      of operations related to remote NFC devices.</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_nfcLlcp">NFC notification from remote LLCP device</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_nfcLlcp">Allows an application to be notified
+      of LLCP operations related to remote NFC devices.</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_disableKeyguard">disable keylock</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_disableKeyguard">Allows an application to disable
@@ -1611,7 +1635,7 @@
 
     <!-- Do not translate.  WebView User Agent string -->
     <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
-        AppleWebKit/534.8 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.8</string>
+        AppleWebKit/534.9 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.9</string>
     <!-- Do not translate.  WebView User Agent targeted content -->
     <string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index ebaaada..6e9530c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -984,4 +984,706 @@
         <item name="android:textColor">@android:color/secondary_text_light</item>
     </style>
 
+    <!-- Begin Holo theme styles -->
+
+    <!-- Text Styles -->
+    <style name="TextAppearance.Holo" parent="TextAppearance">
+    </style>
+
+    <style name="TextAppearance.Holo.Inverse" parent="TextAppearance.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Large" parent="TextAppearance.Large">
+    </style>
+
+    <style name="TextAppearance.Holo.Medium" parent="TextAppearance.Medium">
+    </style>
+
+    <style name="TextAppearance.Holo.Small" parent="TextAppearance.Small">
+    </style>
+
+    <style name="TextAppearance.Holo.Large.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Medium.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Small.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.SearchResult">
+    </style>
+
+    <style name="TextAppearance.Holo.SearchResult.Title">
+    </style>
+
+    <style name="TextAppearance.Holo.SearchResult.Subtitle">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget" parent="TextAppearance.Widget">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.Button" parent="TextAppearance.Holo.Small.Inverse">
+        <item name="android:textColor">@android:color/primary_text_light_nodisable</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.IconMenu.Item" parent="TextAppearance.Holo.Small">
+        <item name="android:textColor">?textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.TabWidget">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">@android:color/tab_indicator_text</item>
+    </style>
+    
+    <style name="TextAppearance.Holo.Widget.TextView">
+        <item name="android:textColor">?textColorPrimaryDisableOnly</item>
+        <item name="android:textColorHint">?textColorHint</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.TextView.PopupMenu">
+        <item name="android:textSize">18sp</item>
+        <item name="android:textColor">?textColorPrimaryDisableOnly</item>
+        <item name="android:textColorHint">?textColorHint</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.DropDownHint">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.DropDownItem">
+        <item name="android:textColor">@android:color/primary_text_light_disable_only</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.TextView.SpinnerItem">
+        <item name="android:textColor">@android:color/primary_text_light_disable_only</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.EditText">
+        <item name="android:textColor">@color/widget_edittext_holo_dark</item>
+        <item name="android:textColorHint">@android:color/hint_foreground_holo_light</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.PopupMenu" parent="TextAppearance.Widget.PopupMenu">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.PopupMenu.Large" parent="TextAppearance.Widget.PopupMenu.Large">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.PopupMenu.Small" parent="TextAppearance.Widget.PopupMenu.Small">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionBar.Title"
+           parent="TextAppearance.Holo.Medium">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionBar.Subtitle"
+           parent="TextAppearance.Holo.Small">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionMode">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionMode.Title" parent="TextAppearance.Widget.ActionMode.Title">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionMode.Subtitle" parent="TextAppearance.Widget.ActionMode.Subtitle">
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.WindowTitle">
+        <item name="android:textColor">#fff</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DialogWindowTitle">
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <!-- Light text styles -->
+    <style name="TextAppearance.Holo.Light" parent="TextAppearance.Holo">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Large">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Medium">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Small">
+    </style>
+ 
+   <style name="TextAppearance.Holo.Light.Large.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Medium.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Small.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.SearchResult" parent="TextAppearance.Holo.SearchResult">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.SearchResult.Title">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.SearchResult.Subtitle">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget" parent="TextAppearance.Widget">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.Button">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.EditText">
+        <item name="android:textColor">@color/widget_edittext_holo_light</item>
+        <item name="android:textColorHint">@android:color/hint_foreground_holo_dark</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.PopupMenu" parent="TextAppearance.Holo.Widget.PopupMenu">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.PopupMenu.Large" parent="TextAppearance.Holo.Widget.PopupMenu.Large">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.PopupMenu.Small" parent="TextAppearance.Holo.Widget.PopupMenu.Small">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.DropDownHint" parent="TextAppearance.Holo.Widget.DropDownHint">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.ActionMode.Title" parent="TextAppearance.Widget.ActionMode.Title">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.ActionMode.Subtitle" parent="TextAppearance.Widget.ActionMode.Subtitle">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.WindowTitle">
+        <item name="android:textColor">#fff</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DialogWindowTitle">
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <!-- Widget Styles -->
+
+    <style name="Widget.Holo" parent="Widget">
+    </style>
+
+    <style name="Widget.Holo.Button" parent="Widget.Button">
+        <item name="android:background">@android:drawable/btn_default_holo_dark</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+        <item name="android:textColor">@android:color/primary_text_holo_dark</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">32dip</item>
+        <item name="android:paddingRight">32dip</item>
+    </style>
+
+    <style name="Widget.Holo.Button.Small">
+        <item name="android:background">@android:drawable/btn_default_holo_dark</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:textColor">@android:color/primary_text_holo_dark</item>
+        <item name="android:minHeight">40dip</item>
+        <item name="android:paddingLeft">24dip</item>
+        <item name="android:paddingRight">24dip</item>
+    </style>
+
+    <style name="Widget.Holo.Button.Inset">
+        <item name="android:background">@android:drawable/button_inset</item>
+    </style>
+
+    <style name="Widget.Holo.Button.Toggle">
+        <item name="android:background">@android:drawable/btn_toggle_holo_dark</item>
+        <item name="android:textOn">@android:string/capital_on</item>
+        <item name="android:textOff">@android:string/capital_off</item>
+        <item name="android:disabledAlpha">?android:attr/disabledAlpha</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">24dip</item>
+        <item name="android:paddingRight">24dip</item>
+    </style>
+
+    <style name="Widget.Holo.TextView" parent="Widget.TextView">
+    </style>
+
+    <style name="Widget.Holo.TextView.ListSeparator" parent="Widget.TextView.ListSeparator">
+    </style>
+
+    <style name="Widget.Holo.TextSelectHandle" parent="Widget.TextSelectHandle">
+    </style>
+
+    <style name="Widget.Holo.AbsListView" parent="Widget.AbsListView">
+    </style>
+
+    <style name="Widget.Holo.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
+    </style>
+
+    <style name="Widget.Holo.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
+    </style>
+
+    <style name="Widget.Holo.ListView.DropDown">
+    </style>
+
+    <style name="Widget.Holo.EditText" parent="Widget.EditText">
+    </style>
+
+    <style name="Widget.Holo.ExpandableListView" parent="Widget.ExpandableListView">
+    </style>
+
+    <style name="Widget.Holo.ExpandableListView.White">
+    </style>
+
+    <style name="Widget.Holo.Gallery" parent="Widget.Gallery">
+    </style>
+
+    <style name="Widget.Holo.GestureOverlayView" parent="Widget.GestureOverlayView">
+    </style>
+
+    <style name="Widget.Holo.GridView" parent="Widget.GridView">
+    </style>
+
+    <style name="Widget.Holo.ImageButton" parent="Widget.ImageButton">
+    </style>
+
+    <style name="Widget.Holo.ImageWell" parent="Widget.ImageWell">
+    </style>
+
+    <style name="Widget.Holo.ListView" parent="Widget.ListView">
+    </style>
+
+    <style name="Widget.Holo.ListView.White">
+    </style>
+
+    <style name="Widget.Holo.PopupWindow" parent="Widget.PopupWindow">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar" parent="Widget.ProgressBar">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Small" parent="Widget.ProgressBar.Horizontal">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Small.Title" parent="Widget.ProgressBar.Small.Title">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Large" parent="Widget.ProgressBar.Large">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Inverse" parent="Widget.ProgressBar.Inverse">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Small.Inverse" parent="Widget.ProgressBar.Small.Inverse">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Large.Inverse" parent="Widget.ProgressBar.Large.Inverse">
+    </style>
+
+    <style name="Widget.Holo.SeekBar" parent="Widget.SeekBar">
+    </style>
+
+    <style name="Widget.Holo.RatingBar" parent="Widget.RatingBar">
+    </style>
+
+    <style name="Widget.Holo.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
+    </style>
+
+    <style name="Widget.Holo.RatingBar.Small">
+    </style>
+
+    <style name="Widget.Holo.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
+    </style>
+
+    <style name="Widget.Holo.ScrollView" parent="Widget.ScrollView">
+    </style>
+
+    <style name="Widget.Holo.HorizontalScrollView" parent="Widget.HorizontalScrollView">
+    </style>
+
+    <style name="Widget.Holo.Spinner" parent="Widget.Spinner.DropDown">
+    </style>
+
+    <style name="Widget.Holo.Spinner.DropDown">
+    </style>
+
+    <style name="Widget.Holo.CompoundButton.Star" parent="Widget.CompoundButton.Star">
+    </style>
+
+    <style name="Widget.Holo.TabWidget" parent="Widget.TabWidget">
+    </style>
+
+    <style name="Widget.Holo.WebTextView" parent="Widget.WebTextView">
+    </style>
+
+    <style name="Widget.Holo.WebView" parent="Widget.WebView">
+    </style>
+
+    <style name="Widget.Holo.DropDownItem" parent="Widget.DropDownItem">
+    </style>
+
+    <style name="Widget.Holo.DropDownItem.Spinner">
+    </style>
+
+    <style name="Widget.Holo.TextView.SpinnerItem" parent="Widget.TextView.SpinnerItem">
+    </style>
+
+    <style name="Widget.Holo.KeyboardView" parent="Widget.KeyboardView">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadge.WindowSmall" parent="Widget.QuickContactBadge.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadge.WindowMedium" parent="Widget.QuickContactBadge.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadge.WindowLarge" parent="Widget.QuickContactBadge.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadgeSmall.WindowSmall" parent="Widget.QuickContactBadgeSmall.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadgeSmall.WindowMedium" parent="Widget.QuickContactBadgeSmall.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.ListPopupWindow" parent="Widget.ListPopupWindow">
+    </style>
+
+    <style name="Widget.Holo.PopupMenu" parent="Widget.PopupMenu">
+    </style>
+
+    <style name="Widget.Holo.ActionButton" parent="Widget.ActionButton">
+    </style>
+
+    <style name="Widget.Holo.ActionButton.Overflow" parent="Widget.ActionButton.Overflow">
+    </style>
+
+    <style name="Widget.Holo.ActionBarView_TabView" parent="Widget.ActionBarView_TabView">
+    </style>
+
+    <style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar">
+    </style>
+
+    <style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText">
+        <item name="android:textAppearance">@style/TextAppearance.Holo.Medium</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <style name="Widget.Holo.ActionMode" parent="Widget.ActionMode">
+        <item name="android:background">@android:drawable/cab_holo_dark</item>
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Subtitle</item>
+    </style>
+
+    <style name="Widget.Holo.ActionButton.CloseMode" parent="Widget.ActionButton.CloseMode">
+        <item name="android:src">@drawable/cab_ic_close_holo</item>
+    </style>
+
+    <style name="Widget.Holo.ActionBar" parent="Widget.ActionBar">
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle</item>
+    </style>
+
+    <!-- Light widget styles -->
+
+    <style name="Widget.Holo.Light">
+    </style>
+
+    <style name="Widget.Holo.Light.Button" parent="Widget.Button">
+    </style>
+
+    <style name="Widget.Holo.Light.Button.Small">
+    </style>
+
+    <style name="Widget.Holo.Light.Button.Inset">
+    </style>
+
+    <style name="Widget.Holo.Light.Button.Toggle">
+        <item name="android:background">@android:drawable/btn_toggle_holo_light</item>
+        <item name="android:textOn">@android:string/capital_on</item>
+        <item name="android:textOff">@android:string/capital_off</item>
+        <item name="android:disabledAlpha">?android:attr/disabledAlpha</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">24dip</item>
+        <item name="android:paddingRight">24dip</item>
+    </style>
+
+    <style name="Widget.Holo.Light.TextView" parent="Widget.TextView">
+    </style>
+
+    <style name="Widget.Holo.Light.TextView.ListSeparator" parent="Widget.TextView.ListSeparator">
+    </style>
+
+    <style name="Widget.Holo.Light.TextSelectHandle" parent="Widget.TextSelectHandle">
+    </style>
+
+    <style name="Widget.Holo.Light.AbsListView" parent="Widget.AbsListView">
+    </style>
+
+    <style name="Widget.Holo.Light.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
+    </style>
+
+    <style name="Widget.Holo.Light.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
+    </style>
+
+    <style name="Widget.Holo.Light.ListView.DropDown">
+    </style>
+
+    <style name="Widget.Holo.Light.EditText" parent="Widget.EditText">
+    </style>
+
+    <style name="Widget.Holo.Light.ExpandableListView" parent="Widget.ExpandableListView">
+    </style>
+
+    <style name="Widget.Holo.Light.ExpandableListView.White">
+    </style>
+
+    <style name="Widget.Holo.Light.Gallery" parent="Widget.Gallery">
+    </style>
+
+    <style name="Widget.Holo.Light.GestureOverlayView" parent="Widget.GestureOverlayView">
+    </style>
+
+    <style name="Widget.Holo.Light.GridView" parent="Widget.GridView">
+    </style>
+
+    <style name="Widget.Holo.Light.ImageButton" parent="Widget.ImageButton">
+    </style>
+
+    <style name="Widget.Holo.Light.ImageWell" parent="Widget.ImageWell">
+    </style>
+
+    <style name="Widget.Holo.Light.ListView" parent="Widget.ListView">
+    </style>
+
+    <style name="Widget.Holo.Light.ListView.White">
+    </style>
+
+    <style name="Widget.Holo.Light.PopupWindow" parent="Widget.PopupWindow">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar" parent="Widget.ProgressBar">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Small" parent="Widget.ProgressBar.Small">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Small.Title" parent="Widget.ProgressBar.Small.Title">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Large" parent="Widget.ProgressBar.Large">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Inverse" parent="Widget.ProgressBar.Inverse">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Small.Inverse" parent="Widget.ProgressBar.Small.Inverse">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Large.Inverse" parent="Widget.ProgressBar.Large.Inverse">
+    </style>
+
+    <style name="Widget.Holo.Light.SeekBar" parent="Widget.SeekBar">
+    </style>
+
+    <style name="Widget.Holo.Light.RatingBar" parent="Widget.RatingBar">
+    </style>
+
+    <style name="Widget.Holo.Light.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
+    </style>
+
+    <style name="Widget.Holo.Light.RatingBar.Small">
+    </style>
+
+    <style name="Widget.Holo.Light.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
+    </style>
+
+    <style name="Widget.Holo.Light.ScrollView" parent="Widget.ScrollView">
+    </style>
+
+    <style name="Widget.Holo.Light.HorizontalScrollView" parent="Widget.HorizontalScrollView">
+    </style>
+
+    <style name="Widget.Holo.Light.Spinner" parent="Widget.Spinner.DropDown">
+    </style>
+
+    <style name="Widget.Holo.Light.Spinner.DropDown">
+    </style>
+
+    <style name="Widget.Holo.Light.CompoundButton.Star" parent="Widget.CompoundButton.Star">
+    </style>
+
+    <style name="Widget.Holo.Light.TabWidget" parent="Widget.TabWidget">
+    </style>
+
+    <style name="Widget.Holo.Light.WebTextView" parent="Widget.WebTextView">
+    </style>
+
+    <style name="Widget.Holo.Light.WebView" parent="Widget.WebView">
+    </style>
+
+    <style name="Widget.Holo.Light.DropDownItem" parent="Widget.DropDownItem">
+    </style>
+
+    <style name="Widget.Holo.Light.DropDownItem.Spinner">
+    </style>
+
+    <style name="Widget.Holo.Light.TextView.SpinnerItem" parent="Widget.TextView.SpinnerItem">
+    </style>
+
+    <style name="Widget.Holo.Light.KeyboardView" parent="Widget.KeyboardView">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadge.WindowSmall" parent="Widget.QuickContactBadge.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadge.WindowMedium" parent="Widget.QuickContactBadge.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadge.WindowLarge" parent="Widget.QuickContactBadge.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadgeSmall.WindowSmall" parent="Widget.QuickContactBadgeSmall.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadgeSmall.WindowMedium" parent="Widget.QuickContactBadgeSmall.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.Light.ListPopupWindow" parent="Widget.ListPopupWindow">
+    </style>
+
+    <style name="Widget.Holo.Light.PopupMenu" parent="Widget.PopupMenu">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionButton" parent="Widget.ActionButton">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionButton.Overflow" parent="Widget.ActionButton.Overflow">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBarView_TabView" parent="Widget.ActionBarView_TabView">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBarView_TabText" parent="Widget.ActionBarView_TabText">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionMode" parent="Widget.ActionMode">
+        <item name="android:background">@android:drawable/cab_holo_light</item>
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Subtitle</item>
+    </style>
+
+    <style name="Widget.Holo.Light.ActionButton.CloseMode" parent="Widget.ActionButton.CloseMode">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBar" parent="Widget.ActionBar">
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle</item>
+    </style>
+
+    <!-- Animation Styles -->
+
+    <style name="Animation.Holo" parent="Animation">
+    </style>
+
+    <style name="Animation.Holo.Activity" parent="Animation.Activity">
+    </style>
+
+    <style name="Animation.Holo.Dialog" parent="Animation.Dialog">
+    </style>
+
+    <!-- Dialog styles -->
+
+    <style name="AlertDialog.Holo" parent="AlertDialog">
+        <item name="fullDark">@android:drawable/dialog_full_holo</item>
+        <item name="topDark">@android:drawable/dialog_top_holo</item>
+        <item name="centerDark">@android:drawable/dialog_middle_holo</item>
+        <item name="bottomDark">@android:drawable/dialog_bottom_holo</item>
+        <item name="fullBright">@android:drawable/dialog_full_holo</item>
+        <item name="topBright">@android:drawable/dialog_top_holo</item>
+        <item name="centerBright">@android:drawable/dialog_middle_holo</item>
+        <item name="bottomBright">@android:drawable/dialog_bottom_holo</item>
+        <item name="bottomMedium">@android:drawable/dialog_bottom_holo</item>
+        <item name="centerMedium">@android:drawable/dialog_middle_holo</item>
+    </style>
+
+    <!-- Window title -->
+    <style name="WindowTitleBackground.Holo">
+        <item name="android:background">@android:drawable/title_bar</item>
+    </style>
+
+    <style name="WindowTitle.Holo">
+        <item name="android:singleLine">true</item>
+        <item name="android:textAppearance">@style/TextAppearance.Holo.WindowTitle</item>
+        <item name="android:shadowColor">#BB000000</item>
+        <item name="android:shadowRadius">2.75</item>
+    </style>
+
+    <style name="DialogWindowTitle.Holo">
+        <item name="android:maxLines">1</item>
+        <item name="android:scrollHorizontally">true</item>
+        <item name="android:textAppearance">@style/TextAppearance.Holo.DialogWindowTitle</item>
+    </style>
+
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 9425fc4..21d91ba 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -57,6 +57,7 @@
         <item name="textColorHighlightInverse">@android:color/highlighted_text_light</item>
         <item name="textColorLink">@android:color/link_text_dark</item>
         <item name="textColorLinkInverse">@android:color/link_text_light</item>
+        <item name="textColorAlertDialogListItem">@android:color/primary_text_light_disable_only</item>
 
         <item name="textAppearanceLarge">@android:style/TextAppearance.Large</item>
         <item name="textAppearanceMedium">@android:style/TextAppearance.Medium</item>
@@ -102,6 +103,8 @@
 
         <item name="activatedBackgroundIndicator">@android:drawable/activated_background</item>
 
+        <item name="listDividerAlertDialog">@android:drawable/divider_horizontal_bright</item>
+
         <item name="expandableListPreferredItemPaddingLeft">40dip</item>
         <item name="expandableListPreferredChildPaddingLeft">
                 ?android:attr/expandableListPreferredItemPaddingLeft</item>
@@ -606,14 +609,230 @@
          holographic theme are translucent on their brackground, so applications
          must ensure that any background they use with this theme is itself
          dark; otherwise, it will be difficult to see the widgets.  The new
-         UI style also includes a full action bar by default. -->
+         UI style also includes a full action bar by default.
+
+         Styles used by the Holo theme are named using the convention Type.Holo.Etc.
+         (For example, Widget.Holo.Button, TextAppearance.Holo.Widget.PopupMenu.Large.)
+         Specific resources used by Holo are named using the convention @type/foo_bar_baz_holo
+         with trailing _dark or _light specifiers if they are not shared between both light and
+         dark versions of the theme. -->
     <style name="Theme.Holo">
-        <item name="editTextBackground">@android:drawable/edit_text_holo_dark</item>
+        <item name="colorForeground">@android:color/bright_foreground_holo_dark</item>
+        <item name="colorForegroundInverse">@android:color/bright_foreground_inverse_holo_dark</item>
+        <item name="colorBackground">@android:color/background_holo_dark</item>
+        <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>
+        <item name="disabledAlpha">0.5</item>
+        <item name="backgroundDimAmount">0.6</item>
+
+        <!-- Text styles -->
+        <item name="textAppearance">@android:style/TextAppearance.Holo</item>
+        <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Inverse</item>
+
+        <item name="textColorPrimary">@android:color/primary_text_holo_dark</item>
+        <item name="textColorSecondary">@android:color/secondary_text_holo_dark</item>
+        <item name="textColorTertiary">@android:color/tertiary_text_holo_dark</item>
+        <item name="textColorPrimaryInverse">@android:color/primary_text_holo_light</item>
+        <item name="textColorSecondaryInverse">@android:color/secondary_text_holo_light</item>
+        <item name="textColorTertiaryInverse">@android:color/tertiary_text_holo_light</item>
+        <item name="textColorPrimaryDisableOnly">@android:color/primary_text_disable_only_holo_dark</item>
+        <item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_disable_only_holo_light</item>
+        <item name="textColorPrimaryNoDisable">@android:color/primary_text_nodisable_holo_dark</item>
+        <item name="textColorSecondaryNoDisable">@android:color/secondary_text_nodisable_holo_dark</item>
+        <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_nodisable_holo_light</item>
+        <item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_nodisable_holo_light</item>
+        <item name="textColorHint">@android:color/hint_foreground_holo_dark</item>
+        <item name="textColorHintInverse">@android:color/hint_foreground_holo_light</item>
+        <item name="textColorSearchUrl">@android:color/search_url_text_holo</item>
+        <item name="textColorHighlight">@android:color/highlighted_text_holo_dark</item>
+        <item name="textColorHighlightInverse">@android:color/highlighted_text_holo_light</item>
+        <item name="textColorLink">@android:color/link_text_holo_dark</item>
+        <item name="textColorLinkInverse">@android:color/link_text_holo_light</item>
+        <item name="textColorAlertDialogListItem">@android:color/primary_text_holo_dark</item>
+
+        <item name="textAppearanceLarge">@android:style/TextAppearance.Holo.Large</item>
+        <item name="textAppearanceMedium">@android:style/TextAppearance.Holo.Medium</item>
+        <item name="textAppearanceSmall">@android:style/TextAppearance.Holo.Small</item>
+        <item name="textAppearanceLargeInverse">@android:style/TextAppearance.Holo.Large.Inverse</item>
+        <item name="textAppearanceMediumInverse">@android:style/TextAppearance.Holo.Medium.Inverse</item>
+        <item name="textAppearanceSmallInverse">@android:style/TextAppearance.Holo.Small.Inverse</item>
+        <item name="textAppearanceSearchResultTitle">@android:style/TextAppearance.Holo.SearchResult.Title</item>
+        <item name="textAppearanceSearchResultSubtitle">@android:style/TextAppearance.Holo.SearchResult.Subtitle</item>
+        
+        <item name="textAppearanceButton">@android:style/TextAppearance.Holo.Widget.Button</item>
+        
         <item name="editTextColor">?android:attr/textColorPrimary</item>
-        <item name="android:windowActionBar">true</item>
-        <item name="android:spinnerStyle">?android:attr/dropDownSpinnerStyle</item>
+        <item name="editTextBackground">@android:drawable/edit_text_holo_dark</item>
+        
+        <item name="candidatesTextStyleSpans">@android:string/candidates_style</item>
+        
+        <item name="textCheckMark">@android:drawable/indicator_check_mark_dark</item>
+        <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_light</item>
+
+        <item name="textAppearanceLargePopupMenu">@android:style/TextAppearance.Holo.Widget.PopupMenu.Large</item>
+        <item name="textAppearanceSmallPopupMenu">@android:style/TextAppearance.Holo.Widget.PopupMenu.Small</item>
+
+        <!-- Button styles -->
+        <item name="buttonStyle">@android:style/Widget.Holo.Button</item>
+
+        <item name="buttonStyleSmall">@android:style/Widget.Holo.Button.Small</item>
+        <item name="buttonStyleInset">@android:style/Widget.Holo.Button.Inset</item>
+
+        <item name="buttonStyleToggle">@android:style/Widget.Holo.Button.Toggle</item>
+
+        <!-- List attributes -->
+        <item name="listPreferredItemHeight">64dip</item>
+        <!-- @hide -->
+        <item name="searchResultListItemHeight">58dip</item>
+        <item name="listDivider">@drawable/divider_horizontal_holo_dark</item>
+        <item name="listSeparatorTextViewStyle">@android:style/Widget.Holo.TextView.ListSeparator</item>   
+
         <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_holo_dark</item>
         <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_holo_dark</item>
+
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background</item>
+
+        <item name="activatedBackgroundIndicator">@android:drawable/activated_background</item>
+
+        <item name="listDividerAlertDialog">@android:drawable/divider_horizontal_holo_dark</item>
+
+        <item name="expandableListPreferredItemPaddingLeft">40dip</item>
+        <item name="expandableListPreferredChildPaddingLeft">
+                ?android:attr/expandableListPreferredItemPaddingLeft</item>
+
+        <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
+        <item name="expandableListPreferredItemIndicatorRight">33dip</item>
+        <item name="expandableListPreferredChildIndicatorLeft">
+                ?android:attr/expandableListPreferredItemIndicatorLeft</item>
+        <item name="expandableListPreferredChildIndicatorRight">
+                ?android:attr/expandableListPreferredItemIndicatorRight</item>
+
+        <!-- Gallery attributes -->
+        <item name="galleryItemBackground">@android:drawable/gallery_item_background</item>
+        
+        <!-- Window attributes -->
+        <item name="windowBackground">@android:drawable/screen_background_holo_dark</item>
+        <item name="windowFrame">@null</item>
+        <item name="windowNoTitle">false</item>
+        <item name="windowFullscreen">false</item>
+        <item name="windowIsFloating">false</item>
+        <item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>
+        <item name="windowShowWallpaper">false</item>
+        <item name="windowTitleStyle">@android:style/WindowTitle.Holo</item>
+        <item name="windowTitleSize">25dip</item>
+        <item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground.Holo</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Holo.Activity</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+        <item name="windowActionBar">true</item>
+        <item name="windowActionModeOverlay">false</item>
+
+        <!-- Dialog attributes -->
+        <item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
+        
+        <!-- Panel attributes -->
+        <item name="panelBackground">@android:drawable/menu_background</item>
+        <item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
+        <!-- These three attributes do not seems to be used by the framework. Declared public though -->
+        <item name="panelColorBackground">#000</item>
+        <item name="panelColorForeground">?android:attr/textColorPrimary</item>
+        <item name="panelTextAppearance">?android:attr/textAppearance</item>
+
+        <!-- Scrollbar attributes -->
+        <item name="scrollbarFadeDuration">250</item>
+        <item name="scrollbarDefaultDelayBeforeFade">300</item> 
+        <item name="scrollbarSize">10dip</item>
+        <item name="scrollbarThumbHorizontal">@android:drawable/scrollbar_handle_horizontal</item>
+        <item name="scrollbarThumbVertical">@android:drawable/scrollbar_handle_vertical</item>
+        <item name="scrollbarTrackHorizontal">@null</item>
+        <item name="scrollbarTrackVertical">@null</item>
+
+        <!-- Text selection handle attributes -->
+        <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.Holo.TextSelectHandle</item>
+
+        <!-- Widget styles -->
+        <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item>
+        <item name="autoCompleteTextViewStyle">@android:style/Widget.Holo.AutoCompleteTextView</item>
+        <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item>
+        <item name="dropDownListViewStyle">@android:style/Widget.Holo.ListView.DropDown</item>
+        <item name="editTextStyle">@android:style/Widget.Holo.EditText</item>
+        <item name="expandableListViewStyle">@android:style/Widget.Holo.ExpandableListView</item>
+        <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.ExpandableListView.White</item>
+        <item name="galleryStyle">@android:style/Widget.Holo.Gallery</item>
+        <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.GestureOverlayView</item>
+        <item name="gridViewStyle">@android:style/Widget.Holo.GridView</item>
+        <item name="imageButtonStyle">@android:style/Widget.Holo.ImageButton</item>
+        <item name="imageWellStyle">@android:style/Widget.Holo.ImageWell</item>
+        <item name="listViewStyle">@android:style/Widget.Holo.ListView</item>
+        <item name="listViewWhiteStyle">@android:style/Widget.Holo.ListView.White</item>
+        <item name="popupWindowStyle">@android:style/Widget.Holo.PopupWindow</item>
+        <item name="progressBarStyle">@android:style/Widget.Holo.ProgressBar</item>
+        <item name="progressBarStyleHorizontal">@android:style/Widget.Holo.ProgressBar.Horizontal</item>
+        <item name="progressBarStyleSmall">@android:style/Widget.Holo.ProgressBar.Small</item>
+        <item name="progressBarStyleSmallTitle">@android:style/Widget.Holo.ProgressBar.Small.Title</item>
+        <item name="progressBarStyleLarge">@android:style/Widget.Holo.ProgressBar.Large</item>
+        <item name="progressBarStyleInverse">@android:style/Widget.Holo.ProgressBar.Inverse</item>
+        <item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.ProgressBar.Small.Inverse</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.ProgressBar.Large.Inverse</item>
+        <item name="seekBarStyle">@android:style/Widget.Holo.SeekBar</item>
+        <item name="ratingBarStyle">@android:style/Widget.Holo.RatingBar</item>
+        <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.RatingBar.Indicator</item>
+        <item name="ratingBarStyleSmall">@android:style/Widget.Holo.RatingBar.Small</item>
+        <item name="radioButtonStyle">@android:style/Widget.Holo.CompoundButton.RadioButton</item>
+        <item name="scrollViewStyle">@android:style/Widget.Holo.ScrollView</item>
+        <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.HorizontalScrollView</item>
+        <item name="spinnerStyle">?android:attr/dropDownSpinnerStyle</item>
+        <item name="dropDownSpinnerStyle">@android:style/Widget.Holo.Spinner.DropDown</item>
+        <item name="starStyle">@android:style/Widget.Holo.CompoundButton.Star</item>
+        <item name="tabWidgetStyle">@android:style/Widget.Holo.TabWidget</item>
+        <item name="textViewStyle">@android:style/Widget.Holo.TextView</item>
+        <item name="webTextViewStyle">@android:style/Widget.Holo.WebTextView</item>
+        <item name="webViewStyle">@android:style/Widget.Holo.WebView</item>
+        <item name="dropDownItemStyle">@android:style/Widget.Holo.DropDownItem</item>
+        <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.DropDownItem.Spinner</item>
+        <item name="spinnerItemStyle">@android:style/Widget.Holo.TextView.SpinnerItem</item>
+        <item name="dropDownHintAppearance">@android:style/TextAppearance.Holo.Widget.DropDownHint</item>
+        <item name="keyboardViewStyle">@android:style/Widget.Holo.KeyboardView</item>
+        <item name="quickContactBadgeStyleWindowSmall">@android:style/Widget.Holo.QuickContactBadge.WindowSmall</item>
+        <item name="quickContactBadgeStyleWindowMedium">@android:style/Widget.Holo.QuickContactBadge.WindowMedium</item>
+        <item name="quickContactBadgeStyleWindowLarge">@android:style/Widget.Holo.QuickContactBadge.WindowLarge</item>
+        <item name="quickContactBadgeStyleSmallWindowSmall">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowSmall</item>
+        <item name="quickContactBadgeStyleSmallWindowMedium">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowMedium</item>
+        <item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowLarge</item>
+        <item name="listPopupWindowStyle">@android:style/Widget.Holo.ListPopupWindow</item>
+        <item name="popupMenuStyle">@android:style/Widget.Holo.PopupMenu</item>
+        
+        <!-- Preference styles -->
+        <item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
+        <item name="preferenceCategoryStyle">@android:style/Preference.Category</item>
+        <item name="preferenceStyle">@android:style/Preference</item>
+        <item name="preferenceInformationStyle">@android:style/Preference.Information</item>
+        <item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>
+        <item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>
+        <item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>
+        <item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>
+        <item name="ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item>
+        <item name="preferenceLayoutChild">@android:layout/preference_child</item>
+
+        <!-- Search widget styles -->
+        <item name="searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item>
+
+        <!-- Action bar styles -->
+        <item name="actionDropDownStyle">@android:style/Widget.Holo.Spinner.DropDown</item>
+        <item name="actionButtonStyle">@android:style/Widget.Holo.ActionButton</item>
+        <item name="actionOverflowButtonStyle">@android:style/Widget.Holo.ActionButton.Overflow</item>
+        <item name="actionButtonPadding">12dip</item>
+        <item name="actionModeBackground">@android:drawable/cab_holo_dark</item>
+        <item name="actionModeCloseDrawable">@android:drawable/cab_ic_close_holo</item>
+        <item name="actionBarTabStyle">@style/Widget.Holo.ActionBarView_TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.Holo.ActionBarView_TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.Holo.ActionBarView_TabText</item>
+        <item name="actionModeStyle">@style/Widget.Holo.ActionMode</item>
+        <item name="actionModeCloseButtonStyle">@style/Widget.Holo.ActionButton.CloseMode</item>
+        <item name="actionBarStyle">@android:style/Widget.Holo.ActionBar</item>
+        <item name="actionBarSize">50dip</item>
+
     </style>
 
     <!-- New Honeycomb holographic theme. Light version.  The widgets in the
@@ -621,14 +840,229 @@
          must ensure that any background they use with this theme is itself
          light; otherwise, it will be difficult to see the widgets.  The new
          UI style also includes a full action bar by default. -->
-    <style name="Theme.Light.Holo">
-        <item name="editTextBackground">@android:drawable/edit_text_holo_light</item>
-        <item name="android:windowActionBar">true</item>
-        <item name="android:spinnerStyle">?android:attr/dropDownSpinnerStyle</item>
-        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_holo_light</item>
-        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_holo_light</item>
-    </style>
+    <style name="Theme.Holo.Light" parent="Theme.Light">
+        <item name="colorForeground">@android:color/bright_foreground_holo_light</item>
+        <item name="colorForegroundInverse">@android:color/bright_foreground_inverse_holo_light</item>
+        <item name="colorBackground">@android:color/background_holo_light</item>
+        <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>
+        <item name="disabledAlpha">0.5</item>
+        <item name="backgroundDimAmount">0.6</item>
+
+        <!-- Text styles -->
+        <item name="textAppearance">@android:style/TextAppearance.Holo.Light</item>
+        <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Light.Inverse</item>
+
+        <item name="textColorPrimary">@android:color/primary_text_holo_light</item>
+        <item name="textColorSecondary">@android:color/secondary_text_holo_light</item>
+        <item name="textColorTertiary">@android:color/tertiary_text_holo_light</item>
+        <item name="textColorPrimaryInverse">@android:color/primary_text_holo_dark</item>
+        <item name="textColorSecondaryInverse">@android:color/secondary_text_holo_dark</item>
+        <item name="textColorTertiaryInverse">@android:color/tertiary_text_holo_dark</item>
+        <item name="textColorPrimaryDisableOnly">@android:color/primary_text_disable_only_holo_light</item>
+        <item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_disable_only_holo_dark</item>
+        <item name="textColorPrimaryNoDisable">@android:color/primary_text_nodisable_holo_light</item>
+        <item name="textColorSecondaryNoDisable">@android:color/secondary_text_nodisable_holo_light</item>
+        <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_nodisable_holo_dark</item>
+        <item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_nodisable_holo_dark</item>
+        <item name="textColorHint">@android:color/hint_foreground_holo_light</item>
+        <item name="textColorHintInverse">@android:color/hint_foreground_holo_dark</item>
+        <item name="textColorSearchUrl">@android:color/search_url_text_holo</item>
+        <item name="textColorHighlight">@android:color/highlighted_text_holo_light</item>
+        <item name="textColorHighlightInverse">@android:color/highlighted_text_holo_dark</item>
+        <item name="textColorLink">@android:color/link_text_holo_light</item>
+        <item name="textColorLinkInverse">@android:color/link_text_holo_dark</item>
+        <item name="textColorAlertDialogListItem">@android:color/primary_text_holo_dark</item>
+
+        <item name="textAppearanceLarge">@android:style/TextAppearance.Holo.Light.Large</item>
+        <item name="textAppearanceMedium">@android:style/TextAppearance.Holo.Light.Medium</item>
+        <item name="textAppearanceSmall">@android:style/TextAppearance.Holo.Light.Small</item>
+        <item name="textAppearanceLargeInverse">@android:style/TextAppearance.Holo.Light.Large.Inverse</item>
+        <item name="textAppearanceMediumInverse">@android:style/TextAppearance.Holo.Light.Medium.Inverse</item>
+        <item name="textAppearanceSmallInverse">@android:style/TextAppearance.Holo.Light.Small.Inverse</item>
+        <item name="textAppearanceSearchResultTitle">@android:style/TextAppearance.Holo.Light.SearchResult.Title</item>
+        <item name="textAppearanceSearchResultSubtitle">@android:style/TextAppearance.Holo.Light.SearchResult.Subtitle</item>
         
+        <item name="textAppearanceButton">@android:style/TextAppearance.Holo.Light.Widget.Button</item>
+        
+        <item name="editTextColor">?android:attr/textColorPrimary</item>
+        <item name="editTextBackground">@android:drawable/edit_text_holo_light</item>
+        
+        <item name="candidatesTextStyleSpans">@android:string/candidates_style</item>
+        
+        <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
+        <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item>
+
+        <item name="textAppearanceLargePopupMenu">@android:style/TextAppearance.Holo.Light.Widget.PopupMenu.Large</item>
+        <item name="textAppearanceSmallPopupMenu">@android:style/TextAppearance.Holo.Light.Widget.PopupMenu.Small</item>
+
+        <!-- Button styles -->
+        <item name="buttonStyle">@android:style/Widget.Holo.Light.Button</item>
+
+        <item name="buttonStyleSmall">@android:style/Widget.Holo.Light.Button.Small</item>
+        <item name="buttonStyleInset">@android:style/Widget.Holo.Light.Button.Inset</item>
+
+        <item name="buttonStyleToggle">@android:style/Widget.Holo.Light.Button.Toggle</item>
+
+        <!-- List attributes -->
+        <item name="listPreferredItemHeight">64dip</item>
+        <!-- @hide -->
+        <item name="searchResultListItemHeight">58dip</item>
+        <item name="listDivider">@drawable/divider_horizontal_holo_dark</item>
+        <item name="listSeparatorTextViewStyle">@android:style/Widget.Holo.Light.TextView.ListSeparator</item>   
+
+        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
+        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
+
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background</item>
+
+        <item name="activatedBackgroundIndicator">@android:drawable/activated_background</item>
+
+        <item name="expandableListPreferredItemPaddingLeft">40dip</item>
+        <item name="expandableListPreferredChildPaddingLeft">
+                ?android:attr/expandableListPreferredItemPaddingLeft</item>
+
+        <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
+        <item name="expandableListPreferredItemIndicatorRight">33dip</item>
+        <item name="expandableListPreferredChildIndicatorLeft">
+                ?android:attr/expandableListPreferredItemIndicatorLeft</item>
+        <item name="expandableListPreferredChildIndicatorRight">
+                ?android:attr/expandableListPreferredItemIndicatorRight</item>
+
+        <item name="listDividerAlertDialog">@android:drawable/divider_horizontal_holo_dark</item>
+
+        <!-- Gallery attributes -->
+        <item name="galleryItemBackground">@android:drawable/gallery_item_background</item>
+        
+        <!-- Window attributes -->
+        <item name="windowBackground">@android:drawable/screen_background_holo_light</item>
+        <item name="windowFrame">@null</item>
+        <item name="windowNoTitle">false</item>
+        <item name="windowFullscreen">false</item>
+        <item name="windowIsFloating">false</item>
+        <item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>
+        <item name="windowShowWallpaper">false</item>
+        <item name="windowTitleStyle">@android:style/WindowTitle.Holo</item>
+        <item name="windowTitleSize">25dip</item>
+        <item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground.Holo</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Holo.Activity</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+        <item name="windowActionBar">true</item>
+        <item name="windowActionModeOverlay">false</item>
+
+        <!-- Dialog attributes -->
+        <item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
+        
+        <!-- Panel attributes -->
+        <item name="panelBackground">@android:drawable/menu_background</item>
+        <item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
+        <!-- These three attributes do not seems to be used by the framework. Declared public though -->
+        <item name="panelColorBackground">#000</item>
+        <item name="panelColorForeground">?android:attr/textColorPrimary</item>
+        <item name="panelTextAppearance">?android:attr/textAppearance</item>
+
+        <!-- Scrollbar attributes -->
+        <item name="scrollbarFadeDuration">250</item>
+        <item name="scrollbarDefaultDelayBeforeFade">300</item>
+        <item name="scrollbarSize">10dip</item>
+        <item name="scrollbarThumbHorizontal">@android:drawable/scrollbar_handle_horizontal</item>
+        <item name="scrollbarThumbVertical">@android:drawable/scrollbar_handle_vertical</item>
+        <item name="scrollbarTrackHorizontal">@null</item>
+        <item name="scrollbarTrackVertical">@null</item>
+
+        <!-- Text selection handle attributes -->
+        <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.Holo.TextSelectHandle</item>
+
+        <!-- Widget styles -->
+        <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item>
+        <item name="autoCompleteTextViewStyle">@android:style/Widget.Holo.AutoCompleteTextView</item>
+        <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item>
+        <item name="dropDownListViewStyle">@android:style/Widget.Holo.ListView.DropDown</item>
+        <item name="editTextStyle">@android:style/Widget.Holo.EditText</item>
+        <item name="expandableListViewStyle">@android:style/Widget.Holo.ExpandableListView</item>
+        <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.ExpandableListView.White</item>
+        <item name="galleryStyle">@android:style/Widget.Holo.Gallery</item>
+        <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.GestureOverlayView</item>
+        <item name="gridViewStyle">@android:style/Widget.Holo.GridView</item>
+        <item name="imageButtonStyle">@android:style/Widget.Holo.ImageButton</item>
+        <item name="imageWellStyle">@android:style/Widget.Holo.ImageWell</item>
+        <item name="listViewStyle">@android:style/Widget.Holo.ListView</item>
+        <item name="listViewWhiteStyle">@android:style/Widget.Holo.ListView.White</item>
+        <item name="popupWindowStyle">@android:style/Widget.Holo.PopupWindow</item>
+        <item name="progressBarStyle">@android:style/Widget.Holo.ProgressBar</item>
+        <item name="progressBarStyleHorizontal">@android:style/Widget.Holo.ProgressBar.Horizontal</item>
+        <item name="progressBarStyleSmall">@android:style/Widget.Holo.ProgressBar.Small</item>
+        <item name="progressBarStyleSmallTitle">@android:style/Widget.Holo.ProgressBar.Small.Title</item>
+        <item name="progressBarStyleLarge">@android:style/Widget.Holo.ProgressBar.Large</item>
+        <item name="progressBarStyleInverse">@android:style/Widget.Holo.ProgressBar.Inverse</item>
+        <item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.ProgressBar.Small.Inverse</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.ProgressBar.Large.Inverse</item>
+        <item name="seekBarStyle">@android:style/Widget.Holo.SeekBar</item>
+        <item name="ratingBarStyle">@android:style/Widget.Holo.RatingBar</item>
+        <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.RatingBar.Indicator</item>
+        <item name="ratingBarStyleSmall">@android:style/Widget.Holo.RatingBar.Small</item>
+        <item name="radioButtonStyle">@android:style/Widget.Holo.CompoundButton.RadioButton</item>
+        <item name="scrollViewStyle">@android:style/Widget.Holo.ScrollView</item>
+        <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.HorizontalScrollView</item>
+        <item name="spinnerStyle">?android:attr/dropDownSpinnerStyle</item>
+        <item name="dropDownSpinnerStyle">@android:style/Widget.Holo.Spinner.DropDown</item>
+        <item name="starStyle">@android:style/Widget.Holo.CompoundButton.Star</item>
+        <item name="tabWidgetStyle">@android:style/Widget.Holo.TabWidget</item>
+        <item name="textViewStyle">@android:style/Widget.Holo.TextView</item>
+        <item name="webTextViewStyle">@android:style/Widget.Holo.WebTextView</item>
+        <item name="webViewStyle">@android:style/Widget.Holo.WebView</item>
+        <item name="dropDownItemStyle">@android:style/Widget.Holo.DropDownItem</item>
+        <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.DropDownItem.Spinner</item>
+        <item name="spinnerItemStyle">@android:style/Widget.Holo.TextView.SpinnerItem</item>
+        <item name="dropDownHintAppearance">@android:style/TextAppearance.Holo.Widget.DropDownHint</item>
+        <item name="keyboardViewStyle">@android:style/Widget.Holo.KeyboardView</item>
+        <item name="quickContactBadgeStyleWindowSmall">@android:style/Widget.Holo.QuickContactBadge.WindowSmall</item>
+        <item name="quickContactBadgeStyleWindowMedium">@android:style/Widget.Holo.QuickContactBadge.WindowMedium</item>
+        <item name="quickContactBadgeStyleWindowLarge">@android:style/Widget.Holo.QuickContactBadge.WindowLarge</item>
+        <item name="quickContactBadgeStyleSmallWindowSmall">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowSmall</item>
+        <item name="quickContactBadgeStyleSmallWindowMedium">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowMedium</item>
+        <item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowLarge</item>
+        <item name="listPopupWindowStyle">@android:style/Widget.Holo.ListPopupWindow</item>
+        <item name="popupMenuStyle">@android:style/Widget.Holo.PopupMenu</item>
+        
+        <!-- Preference styles -->
+        <item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
+        <item name="preferenceCategoryStyle">@android:style/Preference.Category</item>
+        <item name="preferenceStyle">@android:style/Preference</item>
+        <item name="preferenceInformationStyle">@android:style/Preference.Information</item>
+        <item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>
+        <item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>
+        <item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>
+        <item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>
+        <item name="ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item>
+        <item name="preferenceLayoutChild">@android:layout/preference_child</item>
+
+        <!-- Search widget styles -->
+        <item name="searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item>
+
+        <!-- Action bar styles -->
+        <item name="actionDropDownStyle">@android:style/Widget.Holo.Spinner.DropDown</item>
+        <item name="actionButtonStyle">@android:style/Widget.Holo.ActionButton</item>
+        <item name="actionOverflowButtonStyle">@android:style/Widget.Holo.ActionButton.Overflow</item>
+        <item name="actionButtonPadding">12dip</item>
+        <item name="actionModeBackground">@android:drawable/cab_holo_light</item>
+        <item name="actionModeCloseDrawable">@android:drawable/cab_ic_close_holo</item>
+        <item name="actionBarTabStyle">@style/Widget.Holo.ActionBarView_TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.Holo.ActionBarView_TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.Holo.ActionBarView_TabText</item>
+        <item name="actionModeStyle">@style/Widget.Holo.Light.ActionMode</item>
+        <item name="actionModeCloseButtonStyle">@style/Widget.Holo.ActionButton.CloseMode</item>
+        <item name="actionBarStyle">@android:style/Widget.Holo.ActionBar</item>
+        <item name="actionBarSize">50dip</item>
+
+    </style>
+
+    <!-- Development legacy name; if you're using this, switch. -->
+    <style name="Theme.Light.Holo" parent="Theme.Holo.Light">
+    </style>
+    
     <!-- Variant of the holographic (dark) theme with no action bar. -->
     <style name="Theme.Holo.NoActionBar">
         <item name="android:windowActionBar">false</item>
@@ -655,4 +1089,53 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
  
+    <!-- Dialog themes for Holo -->
+
+    <!-- Holo theme for dialog windows and activities, which is used by the
+         {@link android.app.Dialog} class.  This changes the window to be
+         floating (not fill the entire screen), and puts a frame around its
+         contents.  You can set this theme on an activity if you would like to
+         make an activity that looks like a Dialog.
+         This is the default Dialog theme for applications targeting Honeycomb
+         or newer. -->
+    <style name="Theme.Holo.Dialog">
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowTitleStyle">@android:style/DialogWindowTitle.Holo</item>
+        <item name="android:windowBackground">@android:drawable/dialog_full_holo</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Holo.Dialog</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
+        <item name="android:windowActionBar">false</item>
+
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        
+        <item name="textAppearance">@android:style/TextAppearance.Holo</item>
+        <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Inverse</item>
+    </style>
+
+    <!-- Variation of Theme.Holo.Dialog that does not include a frame (or background).
+         The view hierarchy of the dialog is responsible for drawing all of
+         its pixels. -->
+    <style name="Theme.Holo.Dialog.NoFrame">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="android:windowFrame">@null</item>
+        <item name="windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <!-- Holo theme for alert dialog windows, which is used by the
+         {@link android.app.AlertDialog} class.  This is basically a dialog
+         but sets the background to empty so it can do two-tone backgrounds.
+         For applications targeting Honeycomb or newer, this is the default
+         AlertDialog theme. -->
+    <style name="Theme.Holo.Dialog.Alert">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowTitleStyle">@android:style/DialogWindowTitle.Holo</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
 </resources>
diff --git a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
similarity index 99%
rename from core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index a7ec7d5..ed42e64 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.Cursor;
 import android.net.ConnectivityManager;
-import android.net.DownloadManager;
 import android.net.NetworkInfo;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
+import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.Environment;
diff --git a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
similarity index 98%
rename from core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
index a61f02d..38f336e 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.app.DownloadManagerBaseTest.DataType;
+import android.app.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.Cursor;
-import android.net.DownloadManager;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
-import android.net.DownloadManagerBaseTest.DataType;
-import android.net.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
+import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
diff --git a/core/tests/coretests/src/android/net/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
similarity index 97%
rename from core/tests/coretests/src/android/net/DownloadManagerStressTest.java
rename to core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 9fa8620..4ff0295 100644
--- a/core/tests/coretests/src/android/net/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app;
 
 import java.io.File;
 import java.util.Random;
 
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
 import android.database.Cursor;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
+import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 86eda71..39258ae 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -135,28 +135,6 @@
         c.close();
     }
 
-    @SmallTest
-    public void testSetConnectionPoolSize() {
-        mDatabase.enableWriteAheadLogging();
-        // can't set pool size to zero
-        try {
-            mDatabase.setConnectionPoolSize(0);
-            fail("IllegalStateException expected");
-        } catch (IllegalArgumentException e) {
-            assertTrue(e.getMessage().contains("less than the current max value"));
-        }
-        // set pool size to a valid value
-        mDatabase.setConnectionPoolSize(10);
-        assertEquals(10, mDatabase.mConnectionPool.getMaxPoolSize());
-        // can't set pool size to < the value above
-        try {
-            mDatabase.setConnectionPoolSize(1);
-            fail("IllegalStateException expected");
-        } catch (IllegalArgumentException e) {
-            assertTrue(e.getMessage().contains("less than the current max value"));
-        }
-    }
-
     /**
      * a transaction should be started before a standalone-update/insert/delete statement
      */
@@ -299,7 +277,7 @@
             if (useWal) {
                 // set up connection pool
                 mDatabase.enableWriteAheadLogging();
-                mDatabase.setConnectionPoolSize(i + 1);
+                mDatabase.mConnectionPool.setMaxPoolSize(i + 1);
             } else {
                 mDatabase.disableWriteAheadLogging();
             }
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/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index 576765c..7206f4a 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-				   ../../../coretests/src/android/net/DownloadManagerBaseTest.java
+				   ../../../coretests/src/android/app/DownloadManagerBaseTest.java
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
 LOCAL_SDK_VERSION := current
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
index f21e7ac..c0f670b 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java
@@ -15,13 +15,13 @@
  */
 package com.android.frameworks.downloadmanagertests;
 
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.app.DownloadManagerBaseTest;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
-import android.net.DownloadManager;
-import android.net.DownloadManager.Query;
-import android.net.DownloadManager.Request;
-import android.net.DownloadManagerBaseTest;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
diff --git a/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/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/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 2aa3e84..c4421c3 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -601,9 +601,9 @@
             while(mRun) {
                 rbuf[0] = 0;
                 int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
-                if ((msg == 0) && mRun) {
+                if ((msg == 0)) {
                     // Can happen for two reasons
-                    if (rbuf[0] > 0) {
+                    if (rbuf[0] > 0 && mRun) {
                         // 1: Buffer needs to be enlarged.
                         rbuf = new int[rbuf[0] + 2];
                     } else {
@@ -616,6 +616,7 @@
                         } catch(InterruptedException e) {
                         }
                     }
+                    continue;
                 }
                 if(mRS.mMessageCallback != null) {
                     mRS.mMessageCallback.mData = rbuf;
diff --git a/include/camera/CameraHardwareInterface.h b/include/camera/CameraHardwareInterface.h
index 515d879..3a77dd1 100644
--- a/include/camera/CameraHardwareInterface.h
+++ b/include/camera/CameraHardwareInterface.h
@@ -87,7 +87,7 @@
 public:
     virtual ~CameraHardwareInterface() { }
 
-    /** Set the ISurface from which the preview buffers should be dequeued */
+    /** Set the ANativeWindow to which preview frames are sent */
     virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf) = 0;
 
     /** Return the IMemoryHeap for the raw image heap */
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 9a09586..ed2f7d7 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -27,6 +27,7 @@
 
 class MediaSource;
 class AudioTrack;
+class AwesomePlayer;
 
 class AudioPlayer : public TimeSource {
 public:
@@ -35,7 +36,9 @@
         SEEK_COMPLETE
     };
 
-    AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
+    AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink,
+                AwesomePlayer *audioObserver = NULL);
+
     virtual ~AudioPlayer();
 
     // Caller retains ownership of "source".
@@ -91,6 +94,7 @@
     MediaBuffer *mFirstBuffer;
 
     sp<MediaPlayerBase::AudioSink> mAudioSink;
+    AwesomePlayer *mObserver;
 
     static void AudioCallback(int event, void *user, void *info);
     void AudioCallback(int event, void *info);
diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h
new file mode 100644
index 0000000..551ca01
--- /dev/null
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPEG2TS_WRITER_H_
+
+#define MPEG2TS_WRITER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/MediaWriter.h>
+
+namespace android {
+
+struct MPEG2TSWriter : public MediaWriter {
+    MPEG2TSWriter(const char *filename);
+
+    virtual status_t addSource(const sp<MediaSource> &source);
+    virtual status_t start(MetaData *param = NULL);
+    virtual status_t stop();
+    virtual status_t pause();
+    virtual bool reachedEOS();
+    virtual status_t dump(int fd, const Vector<String16>& args);
+
+    void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+    virtual ~MPEG2TSWriter();
+
+private:
+    enum {
+        kWhatSourceNotify = 'noti'
+    };
+
+    struct SourceInfo;
+
+    FILE *mFile;
+    sp<ALooper> mLooper;
+    sp<AHandlerReflector<MPEG2TSWriter> > mReflector;
+
+    bool mStarted;
+
+    Vector<sp<SourceInfo> > mSources;
+    size_t mNumSourcesDone;
+
+    int64_t mNumTSPacketsWritten;
+    int64_t mNumTSPacketsBeforeMeta;
+
+    void writeTS();
+    void writeProgramAssociationTable();
+    void writeProgramMap();
+    void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter);
+};
+
+}  // namespace android
+
+#endif  // MPEG2TS_WRITER_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 1594e31..ab2f11d 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -95,6 +95,8 @@
 
     // Ogg files can be tagged to be automatically looping...
     kKeyAutoLoop          = 'autL',  // bool (int32_t)
+
+    kKeyValidSamples      = 'valD',  // int32_t
 };
 
 enum {
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index a2735a4..436fc38 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -62,7 +62,8 @@
     virtual void finishMediaUpdate() = 0;
     virtual void mountObb(const String16& filename, const String16& key,
             const sp<IObbActionListener>& token) = 0;
-    virtual void unmountObb(const String16& filename, const bool force) = 0;
+    virtual void unmountObb(const String16& filename, const bool force,
+            const sp<IObbActionListener>& token) = 0;
     virtual bool isObbMounted(const String16& filename) = 0;
     virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
 };
diff --git a/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 96b4faed..8d4654f 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.
@@ -351,6 +355,14 @@
      */
     virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
 
+    /* Transfers touch focus from the window associated with one channel to the
+     * window associated with the other channel.
+     *
+     * Returns true on success.  False if the window did not actually have touch focus.
+     */
+    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
+            const sp<InputChannel>& toChannel) = 0;
+
     /* Registers or unregister input channels that may be used as targets for input events.
      * If monitor is true, the channel will receive a copy of all input events.
      *
@@ -405,6 +417,9 @@
     virtual void setFocusedApplication(const InputApplication* inputApplication);
     virtual void setInputDispatchMode(bool enabled, bool frozen);
 
+    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
+            const sp<InputChannel>& toChannel);
+
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
@@ -415,6 +430,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 +448,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 +481,6 @@
             INTERCEPT_KEY_RESULT_CONTINUE,
         };
         InterceptKeyResult interceptKeyResult; // set based on the interception result
-
-        void recycle();
     };
 
     struct MotionSample {
@@ -521,6 +537,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 +575,6 @@
         KeyEntry* keyEntry;
         sp<InputChannel> inputChannel;
         sp<InputApplicationHandle> inputApplicationHandle;
-        int32_t windowType;
         int32_t userActivityEventType;
     };
 
@@ -611,6 +630,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 +646,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 +654,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 +669,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 +848,7 @@
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     Condition mInjectionSyncFinishedCondition;
+    void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
     void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
 
     // Throttling state.
@@ -858,23 +884,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 +939,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 +953,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 +962,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 +993,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/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
index cd8f814..dbc9133 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -40,6 +40,8 @@
     private ScriptC_rslist mScript;
 
     private ArrayList<UnitTest> unitTests;
+    private ListIterator<UnitTest> test_iter;
+    private UnitTest activeTest;
 
     public void init(RenderScriptGL rs, Resources res, int width, int height) {
         mRS = rs;
@@ -54,9 +56,13 @@
         unitTests.add(new UT_primitives(this, mRes));
         unitTests.add(new UT_fp_mad(this, mRes));
         /*
-        unitTests.add(new UnitTest("<Pass>", 1));
+        unitTests.add(new UnitTest(null, "<Pass>", 1));
         unitTests.add(new UnitTest());
-        unitTests.add(new UnitTest("<Fail>", -1));
+        unitTests.add(new UnitTest(null, "<Fail>", -1));
+
+        for (int i = 0; i < 20; i++) {
+            unitTests.add(new UnitTest(null, "<Pass>", 1));
+        }
         */
 
         UnitTest [] uta = new UnitTest[unitTests.size()];
@@ -71,19 +77,6 @@
             uta[i].setItem(listElem);
         }
 
-        /* Run the actual unit tests */
-        ListIterator<UnitTest> test_iter = unitTests.listIterator();
-        while (test_iter.hasNext()) {
-            UnitTest t = test_iter.next();
-            t.start();
-            /*
-            try {
-                t.join();
-            } catch (InterruptedException e) {
-            }
-            */
-        }
-
         mListAllocs.copyAll();
 
         mScript.bind_gList(mListAllocs);
@@ -92,10 +85,40 @@
         mScript.set_gFont(mFont);
 
         mRS.contextBindRootScript(mScript);
-        mRS.finish();
+
+        test_iter = unitTests.listIterator();
+        refreshTestResults(); /* Kick off the first test */
+    }
+
+    static int count = 0;
+    public void checkAndRunNextTest() {
+        if (activeTest != null) {
+            if (!activeTest.isAlive()) {
+                /* Properly clean up on our last test */
+                try {
+                    activeTest.join();
+                }
+                catch (InterruptedException e) {
+                }
+                activeTest = null;
+            }
+        }
+
+        if (activeTest == null) {
+            if (test_iter.hasNext()) {
+                activeTest = test_iter.next();
+                activeTest.start();
+                /* This routine will only get called once when a new test
+                 * should start running. The message handler in UnitTest.java
+                 * ensures this. */
+            }
+        }
+        count++;
     }
 
     public void refreshTestResults() {
+        checkAndRunNextTest();
+
         if (mListAllocs != null && mScript != null && mRS != null) {
             mListAllocs.copyAll();
 
@@ -111,6 +134,7 @@
         mScript.set_gDY(0.0f);
         mLastX = x;
         mLastY = y;
+        refreshTestResults();
     }
 
     public void onActionMove(int x, int y) {
@@ -125,5 +149,6 @@
 
         mLastX = x;
         mLastY = y;
+        refreshTestResults();
     }
 }
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
index d98b763..5eb0d67 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
+++ b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
@@ -16,6 +16,7 @@
 
 package com.android.rs.test;
 import android.renderscript.RenderScript.RSMessage;
+import android.util.Log;
 
 public class UnitTest extends Thread {
     public String name;
@@ -27,11 +28,15 @@
     public static final int RS_MSG_TEST_PASSED = 100;
     public static final int RS_MSG_TEST_FAILED = 101;
 
+    private static int numTests = 0;
+    public int testID;
+
     protected UnitTest(RSTestCore rstc, String n, int initResult) {
         super();
         mRSTC = rstc;
         name = n;
         result = initResult;
+        testID = numTests++;
     }
 
     protected UnitTest(RSTestCore rstc, String n) {
@@ -56,12 +61,20 @@
                     result = -1;
                     break;
                 default:
-                    break;
+                    android.util.Log.v("RenderScript", "Unit test got unexpected message");
+                    return;
             }
 
             if (mItem != null) {
                 mItem.result = result;
-                mRSTC.refreshTestResults();
+                try {
+                    mRSTC.refreshTestResults();
+                }
+                catch (IllegalStateException e) {
+                    /* Ignore the case where our message receiver has been
+                       disconnected. This happens when we leave the application
+                       before it finishes running all of the unit tests. */
+                }
             }
         }
     };
@@ -72,6 +85,9 @@
 
     public void run() {
         /* This method needs to be implemented for each subclass */
+        if (mRSTC != null) {
+            mRSTC.refreshTestResults();
+        }
     }
 }
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
index eb82e56..dfd77e6 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
@@ -170,7 +170,7 @@
 
     // TODO Actually verify test result accuracy
     rsDebug("fp_mad_test PASSED", 0);
-    rsSendToClient(RS_MSG_TEST_PASSED);
+    rsSendToClientBlocking(RS_MSG_TEST_PASSED);
 }
 
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
index 2ba5d52..5312bcc 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
@@ -42,10 +42,10 @@
     failed |= test_primitive_types(index);
 
     if (failed) {
-        rsSendToClient(RS_MSG_TEST_FAILED);
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
     }
     else {
-        rsSendToClient(RS_MSG_TEST_PASSED);
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
     }
 }
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
index 72d1850..a5f0f6b 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
@@ -56,6 +56,27 @@
     int height = rsgGetHeight();
 
     int itemHeight = 80;
+    int totalItemHeight = itemHeight * allocSize;
+
+    /* Prevent scrolling above the top of the list */
+    int firstItem = height - totalItemHeight;
+    if (firstItem < 0) {
+        firstItem = 0;
+    }
+
+    /* Prevent scrolling past the last line of the list */
+    int lastItem = -1 * (totalItemHeight - height);
+    if (lastItem > 0) {
+        lastItem = 0;
+    }
+
+    if (textPos > firstItem) {
+        textPos = firstItem;
+    }
+    else if (textPos < lastItem) {
+        textPos = lastItem;
+    }
+
     int currentYPos = itemHeight + textPos;
 
     for(int i = 0; i < allocSize; i ++) {
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 3681bc2..a7f380f 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -33,6 +33,8 @@
 #include <GLES2/gl2ext.h>
 
 #include <cutils/sched_policy.h>
+#include <sys/syscall.h>
+#include <string.h>
 
 using namespace android;
 using namespace android::renderscript;
@@ -371,11 +373,15 @@
      rsc->mWorkers.mLaunchSignals[idx].init();
      rsc->mWorkers.mNativeThreadId[idx] = gettid();
 
-     //cpu_set_t cpset[16];
-     //int ret = sched_getaffinity(rsc->mWorkers.mNativeThreadId[idx], sizeof(cpset), &cpset);
-     //LOGE("ret = %i", ret);
-
-//sched_setaffinity
+#if 0
+     typedef struct {uint64_t bits[1024 / 64]; } cpu_set_t;
+     cpu_set_t cpuset;
+     memset(&cpuset, 0, sizeof(cpuset));
+     cpuset.bits[idx / 64] |= 1ULL << (idx % 64);
+     int ret = syscall(241, rsc->mWorkers.mNativeThreadId[idx],
+		       sizeof(cpuset), &cpuset);
+     LOGE("SETAFFINITY ret = %i %s", ret, EGLUtils::strerror(ret));
+#endif
 
      setpriority(PRIO_PROCESS, rsc->mWorkers.mNativeThreadId[idx], rsc->mThreadPriority);
      while(rsc->mRunning) {
@@ -490,6 +496,7 @@
         usleep(100);
     }
 
+    mWorkers.mCompleteSignal.init();
     mWorkers.mRunningCount = 0;
     mWorkers.mLaunchCount = 0;
     for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index bce9c13..b85d2a8 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -174,7 +174,7 @@
     uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;}
 
     void launchThreads(WorkerCallback_t cbk, void *data);
-    uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mRunningCount;}
+    uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mCount;}
 
 protected:
     Device *mDev;
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index c3ef356..918625c 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -108,6 +108,11 @@
     rsc->checkError("ProgramVertex::setupGL2 start");
 
     if(!isUserProgram()) {
+        if(mConstants[0].get() == NULL) {
+            LOGE("Unable to set fixed function emulation matrices because allocation is missing");
+            rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+            return;
+        }
         float *f = static_cast<float *>(mConstants[0]->getPtr());
         Matrix mvp;
         mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index f905492..e9621b9 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -201,7 +201,7 @@
         }
 
         //LOGE("usr idx %i, x %i,%i  y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd);
-
+        //LOGE("usr ptr in %p,  out %p", mtls->ptrIn, mtls->ptrOut);
         for (uint32_t y = yStart; y < yEnd; y++) {
             uint32_t offset = mtls->dimX * y;
             uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * offset);
@@ -296,14 +296,12 @@
         mtls.eStrideOut = aout->getType()->getElementSizeBytes();
     }
 
-
-    if ((rsc->getWorkerPoolSize() > 1) && mEnviroment.mIsThreadable &&
-        ((mtls.dimY * mtls.dimZ * mtls.dimArray) > 1)) {
+    if ((rsc->getWorkerPoolSize() > 1) && mEnviroment.mIsThreadable && (mtls.dimY > 1)) {
 
         //LOGE("launch 1");
         rsc->launchThreads(wc_xy, &mtls);
-        //LOGE("launch 2");
     } else {
+        //LOGE("launch 3");
         for (uint32_t ar = mtls.arrayStart; ar < mtls.arrayEnd; ar++) {
             for (uint32_t z = mtls.zStart; z < mtls.zEnd; z++) {
                 for (uint32_t y = mtls.yStart; y < mtls.yEnd; y++) {
@@ -380,11 +378,11 @@
     if (sym) {
         return sym->mPtr;
     }
-    s->mEnviroment.mIsThreadable = false;
     sym = ScriptCState::lookupSymbolCL(name);
     if (sym) {
         return sym->mPtr;
     }
+    s->mEnviroment.mIsThreadable = false;
     sym = ScriptCState::lookupSymbolGL(name);
     if (sym) {
         return sym->mPtr;
diff --git a/libs/rs/rsVertexArray.cpp b/libs/rs/rsVertexArray.cpp
index 075a70d..16be5ea 100644
--- a/libs/rs/rsVertexArray.cpp
+++ b/libs/rs/rsVertexArray.cpp
@@ -84,6 +84,10 @@
 
 void VertexArray::add(const Attrib &a, uint32_t stride)
 {
+    // Skip padding
+    if(a.name[0] == '#') {
+        return;
+    }
     rsAssert(mCount < RS_MAX_ATTRIBS);
     mAttribs[mCount].set(a);
     mAttribs[mCount].buffer = mActiveBuffer;
@@ -94,6 +98,10 @@
 
 void VertexArray::add(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset, const char *name)
 {
+    // Skip padding
+    if(name[0] == '#') {
+        return;
+    }
     rsAssert(mCount < RS_MAX_ATTRIBS);
     mAttribs[mCount].clear();
     mAttribs[mCount].type = type;
@@ -129,12 +137,7 @@
 
     rsc->checkError("VertexArray::setupGL2 disabled");
     for (uint32_t ct=0; ct < mCount; ct++) {
-        int32_t slot = 0;
-
-        if (mAttribs[ct].name[0] == '#') {
-            continue;
-        }
-
+        int32_t slot = -1;
         if (sc->isUserVertexProgram()) {
             slot = sc->vtxAttribSlot(ct);
         } else {
@@ -146,14 +149,11 @@
                 slot = 2;
             } else if (mAttribs[ct].name == "texture0") {
                 slot = 3;
-            } else {
-                continue;
             }
         }
         if(slot < 0) {
             continue;
         }
-
         //logAttrib(ct, slot);
         glEnableVertexAttribArray(slot);
         glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 902bb27..3ad9319 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -429,8 +429,8 @@
         reply.readExceptionCode();
     }
 
-    void mountObb(const String16& filename, const String16& key, const sp<
-            IObbActionListener>& token)
+    void mountObb(const String16& filename, const String16& key,
+            const sp<IObbActionListener>& token)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
@@ -448,7 +448,7 @@
         }
     }
 
-    void unmountObb(const String16& filename, const bool force)
+    void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 1cf7592..16ce24b 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,22 @@
             goto Unresponsive;
         }
 
+        // Figure out whether splitting will be allowed for this window.
+        if (newTouchedWindow
+                && (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 +1095,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 +1224,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 +1266,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 +1282,8 @@
     target.flags = targetFlags;
     target.xOffset = - window->frameLeft;
     target.yOffset = - window->frameTop;
+    target.windowType = window->layoutParamsType;
+    target.pointerIds = pointerIds;
 }
 
 void InputDispatcher::addMonitoringTargetsLocked() {
@@ -1217,22 +1295,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 +1365,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 +1386,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 +1407,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 +1465,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 +1541,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 +1585,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 +1612,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 +1636,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 +1644,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 +1726,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 +1882,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 +1989,9 @@
             eventTime, deviceId, source, policyFlags, action, flags,
             keyCode, scanCode, metaState, downTime);
 #endif
+    if (! validateKeyEvent(action)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1839,6 +2031,9 @@
                 pointerCoords[i].orientation);
     }
 #endif
+    if (! validateMotionEvent(action, pointerCount, pointerIds)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1913,8 +2108,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 +2169,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 +2202,7 @@
             injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
         } else {
             for (;;) {
-                injectionResult = injectedEntry->injectionResult;
+                injectionResult = injectionState->injectionResult;
                 if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
                     break;
                 }
@@ -2025,10 +2222,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 +2242,7 @@
             }
         }
 
-        mAllocator.releaseEventEntry(injectedEntry);
+        mAllocator.releaseInjectionState(injectionState);
     } // release lock
 
 #if DEBUG_INJECTION
@@ -2058,14 +2255,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 +2281,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 +2309,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 +2325,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 +2357,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 +2374,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 +2384,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();
@@ -2305,6 +2473,70 @@
     }
 }
 
+bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
+        const sp<InputChannel>& toChannel) {
+#if DEBUG_FOCUS
+    LOGD("transferTouchFocus: fromChannel=%s, toChannel=%s",
+            fromChannel->getName().string(), toChannel->getName().string());
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        const InputWindow* fromWindow = getWindowLocked(fromChannel);
+        const InputWindow* toWindow = getWindowLocked(toChannel);
+        if (! fromWindow || ! toWindow) {
+#if DEBUG_FOCUS
+            LOGD("Cannot transfer focus because from or to window not found.");
+#endif
+            return false;
+        }
+        if (fromWindow == toWindow) {
+#if DEBUG_FOCUS
+            LOGD("Trivial transfer to same window.");
+#endif
+            return true;
+        }
+
+        bool found = false;
+        for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+            const TouchedWindow& touchedWindow = mTouchState.windows[i];
+            if (touchedWindow.window == fromWindow) {
+                int32_t oldTargetFlags = touchedWindow.targetFlags;
+                BitSet32 pointerIds = touchedWindow.pointerIds;
+
+                mTouchState.windows.removeAt(i);
+
+                int32_t newTargetFlags = 0;
+                if (oldTargetFlags & InputTarget::FLAG_FOREGROUND) {
+                    newTargetFlags |= InputTarget::FLAG_FOREGROUND;
+                    if (toWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+                        newTargetFlags |= InputTarget::FLAG_SPLIT;
+                    }
+                }
+                mTouchState.addOrUpdateWindow(toWindow, newTargetFlags, pointerIds);
+
+                found = true;
+                break;
+            }
+        }
+
+        if (! found) {
+#if DEBUG_FOCUS
+            LOGD("Focus transfer failed because from window did not have focus.");
+#endif
+            return false;
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+    return true;
+}
+
 void InputDispatcher::logDispatchStateLocked() {
     String8 dump;
     dumpDispatchStateLocked(dump);
@@ -2334,12 +2566,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 +2827,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 +2859,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 +2967,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 +2997,7 @@
         ConfigurationChangedEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         mConfigurationChangeEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -2750,6 +3007,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 +3017,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 +3052,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 +3306,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/location/Android.mk b/location/Android.mk
new file mode 100644
index 0000000..12db2f7
--- /dev/null
+++ b/location/Android.mk
@@ -0,0 +1,19 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(TARGET_BUILD_APPS),)
+include $(call all-makefiles-under, $(LOCAL_PATH))
+endif
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
new file mode 100644
index 0000000..a06478a
--- /dev/null
+++ b/location/lib/Android.mk
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+# the library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= com.android.location.provider
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+            $(call all-subdir-java-files)
+
+include $(BUILD_JAVA_LIBRARY)
+
+
+# ====  com.google.location.xml lib def  ========================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := com.android.location.provider.xml
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_CLASS := ETC
+
+# This will install the file in /system/etc/permissions
+#
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+
+include $(BUILD_PREBUILT)
diff --git a/location/lib/com.android.location.provider.xml b/location/lib/com.android.location.provider.xml
new file mode 100644
index 0000000..000e68f
--- /dev/null
+++ b/location/lib/com.android.location.provider.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<permissions>
+    <library name="com.android.location.provider"
+            file="/system/framework/com.android.location.provider.jar" />
+</permissions>
diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java
similarity index 98%
rename from location/java/android/location/provider/GeocodeProvider.java
rename to location/lib/java/com/android/location/provider/GeocodeProvider.java
index 493c631..666bb02 100644
--- a/location/java/android/location/provider/GeocodeProvider.java
+++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.location.provider;
+package com.android.location.provider;
 
 import android.os.IBinder;
 
@@ -80,4 +80,3 @@
         return mProvider;
     }
 }
-
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/lib/java/com/android/location/provider/LocationProvider.java
similarity index 99%
rename from location/java/android/location/provider/LocationProvider.java
rename to location/lib/java/com/android/location/provider/LocationProvider.java
index 14dea14..3714f40 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/lib/java/com/android/location/provider/LocationProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.location.provider;
+package com.android.location.provider;
 
 import android.content.Context;
 import android.net.NetworkInfo;
@@ -356,4 +356,3 @@
      */
     public abstract void onRemoveListener(int uid, WorkSource ws);
 }
-
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 41d2cc5..6aa1ae6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -16,18 +16,19 @@
 
 package android.media;
 
-import java.util.NoSuchElementException;
 import android.app.ActivityManagerNative;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.media.MediaPlayer.OnCompletionListener;
@@ -40,6 +41,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.provider.Settings.System;
 import android.telephony.PhoneStateListener;
@@ -47,7 +49,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.VolumePanel;
-import android.os.SystemProperties;
 
 import com.android.internal.telephony.ITelephony;
 
@@ -58,6 +59,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.Stack;
 
@@ -258,8 +260,8 @@
     // BluetoothHeadset API to control SCO connection
     private BluetoothHeadset mBluetoothHeadset;
 
-    // Bluetooth headset connection state
-    private boolean mBluetoothHeadsetConnected;
+    // Bluetooth headset device
+    private BluetoothDevice mBluetoothHeadsetDevice;
 
     ///////////////////////////////////////////////////////////////////////////
     // Construction
@@ -294,17 +296,20 @@
         AudioSystem.setErrorCallback(mAudioSystemCallback);
         loadSoundEffects();
 
-        mBluetoothHeadsetConnected = false;
-        mBluetoothHeadset = new BluetoothHeadset(context,
-                                                 mBluetoothHeadsetServiceListener);
+        mBluetoothHeadsetDevice = null;
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
 
         // Register for device connection intent broadcasts.
         IntentFilter intentFilter =
                 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
-        intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
-        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
+        intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
         context.registerReceiver(mReceiver, intentFilter);
 
         // Register for media button intent broadcasts.
@@ -1000,7 +1005,7 @@
 
         public void incCount() {
             synchronized(mScoClients) {
-                requestScoState(BluetoothHeadset.AUDIO_STATE_CONNECTED);
+                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
                 if (mStartcount == 0) {
                     try {
                         mCb.linkToDeath(this, 0);
@@ -1026,7 +1031,7 @@
                             Log.w(TAG, "decCount() going to 0 but not registered to binder");
                         }
                     }
-                    requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
+                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                 }
             }
         }
@@ -1042,7 +1047,7 @@
                 }
                 mStartcount = 0;
                 if (stopSco) {
-                    requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
+                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                 }
             }
         }
@@ -1068,12 +1073,12 @@
 
         private void requestScoState(int state) {
             if (totalCount() == 0 &&
-                mBluetoothHeadsetConnected &&
+                mBluetoothHeadsetDevice != null &&
                 AudioService.this.mMode == AudioSystem.MODE_NORMAL) {
-                if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) {
-                    mBluetoothHeadset.startVoiceRecognition();
+                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+                    mBluetoothHeadset.startVoiceRecognition(mBluetoothHeadsetDevice);
                 } else {
-                    mBluetoothHeadset.stopVoiceRecognition();
+                    mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice);
                 }
             }
         }
@@ -1103,23 +1108,27 @@
         }
     }
 
-    private BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener =
-        new BluetoothHeadset.ServiceListener() {
-        public void onServiceConnected() {
-            if (mBluetoothHeadset != null) {
-                BluetoothDevice device = mBluetoothHeadset.getCurrentHeadset();
-                if (mBluetoothHeadset.getState(device) == BluetoothHeadset.STATE_CONNECTED) {
-                    mBluetoothHeadsetConnected = true;
-                }
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mBluetoothHeadset = (BluetoothHeadset) proxy;
+            Set<BluetoothDevice> deviceSet = mBluetoothHeadset.getConnectedDevices();
+            if (deviceSet.size() > 0) {
+                BluetoothDevice[] devices =
+                    deviceSet.toArray(new BluetoothDevice[deviceSet.size()]);
+                mBluetoothHeadsetDevice = devices[0];
+            } else {
+                mBluetoothHeadsetDevice = null;
             }
         }
-        public void onServiceDisconnected() {
+        public void onServiceDisconnected(int profile) {
             if (mBluetoothHeadset != null) {
-                BluetoothDevice device = mBluetoothHeadset.getCurrentHeadset();
-                if (mBluetoothHeadset.getState(device) == BluetoothHeadset.STATE_DISCONNECTED) {
-                    mBluetoothHeadsetConnected = false;
+                Set<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
+                if (devices.size() == 0) {
+                    mBluetoothHeadsetDevice = null;
                     clearAllScoClients();
                 }
+                mBluetoothHeadset = null;
             }
         }
     };
@@ -1813,18 +1822,18 @@
                         config = AudioSystem.FORCE_NONE;
                 }
                 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE,
-                                               BluetoothA2dp.STATE_DISCONNECTED);
+            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                                               BluetoothProfile.STATE_DISCONNECTED);
                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                 String address = btDevice.getAddress();
-                boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
-                                       ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
+                boolean isConnected =
+                    (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
+                     mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
 
-                if (isConnected &&
-                    state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
+                if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                     if (btDevice.isBluetoothDock()) {
-                        if (state == BluetoothA2dp.STATE_DISCONNECTED) {
+                        if (state == BluetoothProfile.STATE_DISCONNECTED) {
                             // introduction of a delay for transient disconnections of docks when
                             // power is rapidly turned off/on, this message will be canceled if
                             // we reconnect the dock under a preset delay
@@ -1834,9 +1843,7 @@
                     } else {
                         makeA2dpDeviceUnavailableNow(address);
                     }
-                } else if (!isConnected &&
-                             (state == BluetoothA2dp.STATE_CONNECTED ||
-                              state == BluetoothA2dp.STATE_PLAYING)) {
+                } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                     if (btDevice.isBluetoothDock()) {
                         // this could be a reconnection after a transient disconnection
                         cancelA2dpDeviceTimeout();
@@ -1851,9 +1858,9 @@
                     }
                     makeA2dpDeviceAvailable(address);
                 }
-            } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                                               BluetoothHeadset.STATE_ERROR);
+            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                                               BluetoothProfile.STATE_DISCONNECTED);
                 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                 String address = null;
@@ -1874,21 +1881,21 @@
                 }
 
                 boolean isConnected = (mConnectedDevices.containsKey(device) &&
-                                       ((String)mConnectedDevices.get(device)).equals(address));
+                                       mConnectedDevices.get(device).equals(address));
 
-                if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
+                if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                     AudioSystem.setDeviceConnectionState(device,
                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                                          address);
                     mConnectedDevices.remove(device);
-                    mBluetoothHeadsetConnected = false;
+                    mBluetoothHeadsetDevice = null;
                     clearAllScoClients();
-                } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
+                } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                     AudioSystem.setDeviceConnectionState(device,
                                                          AudioSystem.DEVICE_STATE_AVAILABLE,
                                                          address);
                     mConnectedDevices.put(new Integer(device), address);
-                    mBluetoothHeadsetConnected = true;
+                    mBluetoothHeadsetDevice = btDevice;
                 }
             } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
                 int state = intent.getIntExtra("state", 0);
@@ -1922,15 +1929,14 @@
                     }
                 }
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
-                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE,
-                                               BluetoothHeadset.STATE_ERROR);
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                 synchronized (mScoClients) {
                     if (!mScoClients.isEmpty()) {
                         switch (state) {
-                        case BluetoothHeadset.AUDIO_STATE_CONNECTED:
+                        case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                             state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                             break;
-                        case BluetoothHeadset.AUDIO_STATE_DISCONNECTED:
+                        case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
                             state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
                             break;
                         default:
diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/MtpCursor.java
index b9dd03e..ff8799a 100644
--- a/media/java/android/media/MtpCursor.java
+++ b/media/java/android/media/MtpCursor.java
@@ -31,7 +31,7 @@
     static final String TAG = "MtpCursor";
     static final int NO_COUNT = -1;
 
-    /* constants for mQueryType */
+    /* constants for queryType */
     public static final int DEVICE              = 1;
     public static final int DEVICE_ID           = 2;
     public static final int STORAGE             = 3;
@@ -41,27 +41,15 @@
     public static final int STORAGE_CHILDREN    = 7;
     public static final int OBJECT_CHILDREN     = 8;
 
-    private int mQueryType;
-    private int mDeviceID;
-    private long mStorageID;
-    private long mQbjectID;
-
     /** The names of the columns in the projection */
     private String[] mColumns;
 
     /** The number of rows in the cursor */
     private int mCount = NO_COUNT;
 
-    private final MtpClient mClient;
 
     public MtpCursor(MtpClient client, int queryType, int deviceID, long storageID, long objectID,
             String[] projection) {
-
-        mClient = client;
-        mQueryType = queryType;
-        mDeviceID = deviceID;
-        mStorageID = storageID;
-        mQbjectID = objectID;
         mColumns = projection;
 
         HashMap<String, Integer> map;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 403ed58..630d7112e 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -25,8 +25,9 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.provider.MediaStore.Audio;
-import android.provider.MediaStore.MediaColumns;
 import android.provider.MediaStore.Files;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.MediaColumns;
 import android.provider.Mtp;
 import android.util.Log;
 
@@ -278,8 +279,9 @@
         return null;
     }
 
-    private int[] getSupportedObjectProperties(int handle) {
-        return new int[] {
+    static final int[] FILE_PROPERTIES = {
+            // NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
+            // and IMAGE_PROPERTIES below
             MtpConstants.PROPERTY_STORAGE_ID,
             MtpConstants.PROPERTY_OBJECT_FORMAT,
             MtpConstants.PROPERTY_PROTECTION_STATUS,
@@ -289,7 +291,93 @@
             MtpConstants.PROPERTY_PARENT_OBJECT,
             MtpConstants.PROPERTY_PERSISTENT_UID,
             MtpConstants.PROPERTY_NAME,
-        };
+            MtpConstants.PROPERTY_DATE_ADDED,
+    };
+
+    static final int[] AUDIO_PROPERTIES = {
+            // NOTE must match FILE_PROPERTIES above
+            MtpConstants.PROPERTY_STORAGE_ID,
+            MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
+            MtpConstants.PROPERTY_OBJECT_SIZE,
+            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
+            MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
+            MtpConstants.PROPERTY_DISPLAY_NAME,
+            MtpConstants.PROPERTY_DATE_ADDED,
+
+            // audio specific properties
+            MtpConstants.PROPERTY_ARTIST,
+            MtpConstants.PROPERTY_ALBUM_NAME,
+            MtpConstants.PROPERTY_ALBUM_ARTIST,
+            MtpConstants.PROPERTY_TRACK,
+            MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE,
+            MtpConstants.PROPERTY_DURATION,
+            MtpConstants.PROPERTY_GENRE,
+            MtpConstants.PROPERTY_COMPOSER,
+    };
+
+    static final int[] VIDEO_PROPERTIES = {
+            // NOTE must match FILE_PROPERTIES above
+            MtpConstants.PROPERTY_STORAGE_ID,
+            MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
+            MtpConstants.PROPERTY_OBJECT_SIZE,
+            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
+            MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
+            MtpConstants.PROPERTY_DISPLAY_NAME,
+            MtpConstants.PROPERTY_DATE_ADDED,
+
+            // video specific properties
+            MtpConstants.PROPERTY_ARTIST,
+            MtpConstants.PROPERTY_ALBUM_NAME,
+            MtpConstants.PROPERTY_DURATION,
+            MtpConstants.PROPERTY_DESCRIPTION,
+    };
+
+    static final int[] IMAGE_PROPERTIES = {
+            // NOTE must match FILE_PROPERTIES above
+            MtpConstants.PROPERTY_STORAGE_ID,
+            MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
+            MtpConstants.PROPERTY_OBJECT_SIZE,
+            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
+            MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
+            MtpConstants.PROPERTY_DISPLAY_NAME,
+            MtpConstants.PROPERTY_DATE_ADDED,
+
+            // image specific properties
+            MtpConstants.PROPERTY_DESCRIPTION,
+    };
+
+    private int[] getSupportedObjectProperties(int format) {
+        switch (format) {
+            case MtpConstants.FORMAT_MP3:
+            case MtpConstants.FORMAT_WAV:
+            case MtpConstants.FORMAT_WMA:
+            case MtpConstants.FORMAT_OGG:
+            case MtpConstants.FORMAT_AAC:
+                return AUDIO_PROPERTIES;
+            case MtpConstants.FORMAT_MPEG:
+            case MtpConstants.FORMAT_3GP_CONTAINER:
+            case MtpConstants.FORMAT_WMV:
+                return VIDEO_PROPERTIES;
+            case MtpConstants.FORMAT_EXIF_JPEG:
+            case MtpConstants.FORMAT_GIF:
+            case MtpConstants.FORMAT_PNG:
+            case MtpConstants.FORMAT_BMP:
+                return IMAGE_PROPERTIES;
+            default:
+                return FILE_PROPERTIES;
+        }
     }
 
     private int[] getSupportedDeviceProperties() {
@@ -299,17 +387,90 @@
         };
     }
 
+    private String queryString(int id, String column) {
+        Cursor c = null;
+        try {
+            // for now we are only reading properties from the "objects" table
+            c = mMediaProvider.query(mObjectsUri,
+                            new String [] { Files.FileColumns._ID, column },
+                            ID_WHERE, new String[] { Integer.toString(id) }, null);
+            if (c != null && c.moveToNext()) {
+                return c.getString(1);
+            } else {
+                return "";
+            }
+        } catch (Exception e) {
+            return null;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private String queryGenre(int id) {
+        Cursor c = null;
+        try {
+            Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
+            c = mMediaProvider.query(uri,
+                            new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
+                            null, null, null);
+            if (c != null && c.moveToNext()) {
+                return c.getString(1);
+            } else {
+                return "";
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "queryGenre exception", e);
+            return null;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private boolean queryInt(int id, String column, long[] outValue) {
+        Cursor c = null;
+        try {
+            // for now we are only reading properties from the "objects" table
+            c = mMediaProvider.query(mObjectsUri,
+                            new String [] { Files.FileColumns._ID, column },
+                            ID_WHERE, new String[] { Integer.toString(id) }, null);
+            if (c != null && c.moveToNext()) {
+                outValue[0] = c.getLong(1);
+                return true;
+            }
+            return false;
+        } catch (Exception e) {
+            return false;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private String nameFromPath(String path) {
+        // extract name from full path
+        int start = 0;
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            start = lastSlash + 1;
+        }
+        int end = path.length();
+        if (end - start > 255) {
+            end = start + 255;
+        }
+        return path.substring(start, end);
+    }
+
     private int getObjectProperty(int handle, int property,
                             long[] outIntValue, char[] outStringValue) {
         Log.d(TAG, "getObjectProperty: " + property);
         String column = null;
         boolean isString = false;
 
-        // temporary hack
-        if (property == MtpConstants.PROPERTY_NAME) {
-            property = MtpConstants.PROPERTY_OBJECT_FILE_NAME;
-        }
-
         switch (property) {
             case MtpConstants.PROPERTY_STORAGE_ID:
                 outIntValue[0] = mStorageID;
@@ -325,12 +486,46 @@
                 column = Files.FileColumns.SIZE;
                 break;
             case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
-                column = Files.FileColumns.DATA;
-                isString = true;
-                break;
+                // special case - need to extract file name from full path
+                String value = queryString(handle, Files.FileColumns.DATA);
+                if (value != null) {
+                    value = nameFromPath(value);
+                    value.getChars(0, value.length(), outStringValue, 0);
+                    outStringValue[value.length()] = 0;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
+            case MtpConstants.PROPERTY_NAME:
+                // first try title
+                String name = queryString(handle, MediaColumns.TITLE);
+                // then try name
+                if (name == null) {
+                    name = queryString(handle, Audio.PlaylistsColumns.NAME);
+                }
+                // if title and name fail, extract name from full path
+                if (name == null) {
+                    name = queryString(handle, Files.FileColumns.DATA);
+                    if (name != null) {
+                        name = nameFromPath(name);
+                    }
+                }
+                if (name != null) {
+                    name.getChars(0, name.length(), outStringValue, 0);
+                    outStringValue[name.length()] = 0;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
             case MtpConstants.PROPERTY_DATE_MODIFIED:
                 column = Files.FileColumns.DATE_MODIFIED;
                 break;
+            case MtpConstants.PROPERTY_DATE_ADDED:
+                column = Files.FileColumns.DATE_ADDED;
+                break;
+            case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
+                column = Audio.AudioColumns.YEAR;
+                break;
             case MtpConstants.PROPERTY_PARENT_OBJECT:
                 column = Files.FileColumns.PARENT;
                 break;
@@ -341,44 +536,64 @@
                 puid += handle;
                 outIntValue[0] = puid;
                 return MtpConstants.RESPONSE_OK;
+            case MtpConstants.PROPERTY_DURATION:
+                column = Audio.AudioColumns.DURATION;
+                break;
+            case MtpConstants.PROPERTY_TRACK:
+                if (queryInt(handle, Audio.AudioColumns.TRACK, outIntValue)) {
+                    // track is stored in lower 3 decimal digits
+                    outIntValue[0] %= 1000;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
+            case MtpConstants.PROPERTY_DISPLAY_NAME:
+                column = MediaColumns.DISPLAY_NAME;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_ARTIST:
+                column = Audio.AudioColumns.ARTIST;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_ALBUM_NAME:
+                column = Audio.AudioColumns.ALBUM;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_ALBUM_ARTIST:
+                column = Audio.AudioColumns.ALBUM_ARTIST;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_GENRE:
+                String genre = queryGenre(handle);
+                if (genre != null) {
+                    genre.getChars(0, genre.length(), outStringValue, 0);
+                    outStringValue[genre.length()] = 0;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
+            case MtpConstants.PROPERTY_COMPOSER:
+                column = Audio.AudioColumns.COMPOSER;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_DESCRIPTION:
+                column = Images.ImageColumns.DESCRIPTION;
+                isString = true;
+                break;
             default:
                 return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
         }
 
-        Cursor c = null;
-        try {
-            // for now we are only reading properties from the "objects" table
-            c = mMediaProvider.query(mObjectsUri,
-                            new String [] { Files.FileColumns._ID, column },
-                            ID_WHERE, new String[] { Integer.toString(handle) }, null);
-            if (c != null && c.moveToNext()) {
-                if (isString) {
-                    String value = c.getString(1);
-                    int start = 0;
-
-                    if (property == MtpConstants.PROPERTY_OBJECT_FILE_NAME) {
-                        // extract name from full path
-                        int lastSlash = value.lastIndexOf('/');
-                        if (lastSlash >= 0) {
-                            start = lastSlash + 1;
-                        }
-                    }
-                    int end = value.length();
-                    if (end - start > 255) {
-                        end = start + 255;
-                    }
-                    value.getChars(start, end, outStringValue, 0);
-                    outStringValue[end - start] = 0;
-                } else {
-                    outIntValue[0] = c.getLong(1);
-                }
+        if (isString) {
+            String value = queryString(handle, column);
+            if (value != null) {
+                value.getChars(0, value.length(), outStringValue, 0);
+                outStringValue[value.length()] = 0;
                 return MtpConstants.RESPONSE_OK;
             }
-        } catch (Exception e) {
-            return MtpConstants.RESPONSE_GENERAL_ERROR;
-        } finally {
-            if (c != null) {
-                c.close();
+        } else {
+            if (queryInt(handle, column, outIntValue)) {
+                return MtpConstants.RESPONSE_OK;
             }
         }
         // query failed if we get here
diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java
index 9d40a78..468ba2a 100755
--- a/media/java/android/media/videoeditor/AudioTrack.java
+++ b/media/java/android/media/videoeditor/AudioTrack.java
@@ -35,6 +35,11 @@
     private long mEndBoundaryTimeMs;

     private boolean mLoop;

 

+    private final int mAudioChannels;

+    private final int mAudioType;

+    private final int mAudioBitrate;

+    private final int mAudioSamplingFrequency;

+

     // Ducking variables

     private int mDuckingThreshold;

     private int mDuckingLowVolume;

@@ -54,7 +59,6 @@
 

     /**

      * Constructor

-     *

      * @param audioTrackId The AudioTrack id

      * @param filename The absolute file name

      *

@@ -68,6 +72,13 @@
         mStartTimeMs = 0;

         // TODO: This value represents to the duration of the audio file

         mDurationMs = 300000;

+        // TODO: This value needs to be read from the audio track of the source

+        // file

+        mAudioChannels = 2;

+        mAudioType = MediaProperties.ACODEC_AAC_LC;

+        mAudioBitrate = 128000;

+        mAudioSamplingFrequency = 44100;

+

         mTimelineDurationMs = mDurationMs;

         mVolumePercent = 100;

 

@@ -104,6 +115,34 @@
     }

 

     /**

+     * @return The number of audio channels in the source of this audio track

+     */

+    public int getAudioChannels() {

+        return mAudioChannels;

+    }

+

+    /**

+     * @return The audio codec of the source of this audio track

+     */

+    public int getAudioType() {

+        return mAudioType;

+    }

+

+    /**

+     * @return The audio sample frequency of the audio track

+     */

+    public int getAudioSamplingFrequency() {

+        return mAudioSamplingFrequency;

+    }

+

+    /**

+     * @return The audio bitrate of the audio track

+     */

+    public int getAudioBitrate() {

+        return mAudioBitrate;

+    }

+

+    /**

      * Set the volume of this audio track as percentage of the volume in the

      * original audio source file.

      *

@@ -112,8 +151,8 @@
      *            is same as original volume. It it is set to 200, then volume

      *            is doubled (provided that volume amplification is supported)

      *

-     * @throws UnsupportedOperationException if volume amplification is requested

-     *             and is not supported.

+     * @throws UnsupportedOperationException if volume amplification is

+     *             requested and is not supported.

      */

     public void setVolume(int volumePercent) {

         mVolumePercent = volumePercent;

@@ -209,8 +248,8 @@
 

     /**

      * Enable the loop mode for this audio track. Note that only one of the

-     * audio tracks in the timeline can have the loop mode enabled. When

-     * looping is enabled the samples between mBeginBoundaryTimeMs and

+     * audio tracks in the timeline can have the loop mode enabled. When looping

+     * is enabled the samples between mBeginBoundaryTimeMs and

      * mEndBoundaryTimeMs are looped.

      */

     public void enableLoop() {

@@ -285,18 +324,19 @@
      */

     public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)

             throws IOException {

-        // TODO: Set mAudioWaveformFilename at the end once the extract is complete

+        // TODO: Set mAudioWaveformFilename at the end once the extract is

+        // complete

     }

 

     /**

      * Get the audio waveform file name if extractAudioWaveform was successful.

      * The file format is as following:

      * <ul>

-     *  <li>first 4 bytes provide the number of samples for each value, as

-     *      big-endian signed</li>

-     *  <li>4 following bytes is the total number of values in the file, as

-     *      big-endian signed</li>

-     *  <li>then, all values follow as bytes</li>

+     * <li>first 4 bytes provide the number of samples for each value, as

+     * big-endian signed</li>

+     * <li>4 following bytes is the total number of values in the file, as

+     * big-endian signed</li>

+     * <li>then, all values follow as bytes</li>

      * </ul>

      *

      * @return the name of the file, null if the file does not exist

diff --git a/media/java/android/media/videoeditor/Effect.java b/media/java/android/media/videoeditor/Effect.java
index 8c39577..038bfc2 100755
--- a/media/java/android/media/videoeditor/Effect.java
+++ b/media/java/android/media/videoeditor/Effect.java
@@ -26,6 +26,8 @@
 public abstract class Effect {

     // Instance variables

     private final String mUniqueId;

+    // The effect owner

+    private final MediaItem mMediaItem;

     protected long mDurationMs;

     // The start time of the effect relative to the media item timeline

     protected long mStartTimeMs;

@@ -35,6 +37,7 @@
      */

     @SuppressWarnings("unused")

     private Effect() {

+        mMediaItem = null;

         mUniqueId = null;

         mStartTimeMs = 0;

         mDurationMs = 0;

@@ -43,12 +46,22 @@
     /**

      * Constructor

      *

+     * @param mediaItem The media item owner

      * @param effectId The effect id

      * @param startTimeMs The start time relative to the media item to which it

      *            is applied

      * @param durationMs The effect duration in milliseconds

      */

-    public Effect(String effectId, long startTimeMs, long durationMs) {

+    public Effect(MediaItem mediaItem, String effectId, long startTimeMs, long durationMs) {

+        if (mediaItem == null) {

+            throw new IllegalArgumentException("Media item cannot be null");

+        }

+

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

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

+        }

+

+        mMediaItem = mediaItem;

         mUniqueId = effectId;

         mStartTimeMs = startTimeMs;

         mDurationMs = durationMs;

@@ -68,7 +81,13 @@
      * @param durationMs of the effect in milliseconds

      */

     public void setDuration(long durationMs) {

+        if (mStartTimeMs + durationMs > mMediaItem.getTimelineDuration()) {

+            throw new IllegalArgumentException("Duration is too large");

+        }

+

         mDurationMs = durationMs;

+

+        mMediaItem.invalidateTransitions(this);

     }

 

     /**

@@ -88,7 +107,13 @@
      *            of the media item in milliseconds

      */

     public void setStartTime(long startTimeMs) {

+        if (startTimeMs + mDurationMs > mMediaItem.getTimelineDuration()) {

+            throw new IllegalArgumentException("Start time is too large");

+        }

+

         mStartTimeMs = startTimeMs;

+

+        mMediaItem.invalidateTransitions(this);

     }

 

     /**

@@ -98,6 +123,13 @@
         return mStartTimeMs;

     }

 

+    /**

+     * @return The media item owner

+     */

+    public MediaItem getMediaItem() {

+        return mMediaItem;

+    }

+

     /*

      * {@inheritDoc}

      */

diff --git a/media/java/android/media/videoeditor/EffectColor.java b/media/java/android/media/videoeditor/EffectColor.java
index 7c61627..f38cf75 100755
--- a/media/java/android/media/videoeditor/EffectColor.java
+++ b/media/java/android/media/videoeditor/EffectColor.java
@@ -61,12 +61,13 @@
      */

     @SuppressWarnings("unused")

     private EffectColor() {

-        this(null, 0, 0, 0, 0);

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

     }

 

     /**

      * Constructor

      *

+     * @param mediaItem The media item owner

      * @param effectId The effect id

      * @param startTimeMs The start time relative to the media item to which it

      *            is applied

@@ -77,9 +78,9 @@
      * @param param if type is TYPE_COLOR, param is the RGB color as 888.

      *            Otherwise, param is ignored

      */

-    public EffectColor(String effectId, long startTimeMs, long durationMs,

+    public EffectColor(MediaItem mediaItem, String effectId, long startTimeMs, long durationMs,

             int type, int param) {

-        super(effectId, startTimeMs, durationMs);

+        super(mediaItem, effectId, startTimeMs, durationMs);

         mType = type;

         mParam = param;

     }

diff --git a/media/java/android/media/videoeditor/EffectKenBurns.java b/media/java/android/media/videoeditor/EffectKenBurns.java
index c6d22f4..fd2da5c 100755
--- a/media/java/android/media/videoeditor/EffectKenBurns.java
+++ b/media/java/android/media/videoeditor/EffectKenBurns.java
@@ -35,22 +35,23 @@
      */

     @SuppressWarnings("unused")

     private EffectKenBurns() throws IOException {

-        this(null, null, null, 0, 0);

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

     }

 

     /**

      * Constructor

      *

+     * @param mediaItem The media item owner

      * @param effectId The effect id

      * @param startRect The start rectangle

      * @param endRect The end rectangle

      * @param startTimeMs The start time

      * @param durationMs The duration of the Ken Burns effect in milliseconds

      */

-    public EffectKenBurns(String effectId, Rect startRect, Rect endRect, long startTime,

-            long durationMs)

+    public EffectKenBurns(MediaItem mediaItem, String effectId, Rect startRect, Rect endRect,

+            long startTime, long durationMs)

             throws IOException {

-        super(effectId, startTime, durationMs);

+        super(mediaItem, effectId, startTime, durationMs);

 

         mStartRect = startRect;

         mEndRect = endRect;

diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index 9c39b20..ab23119 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -17,6 +17,7 @@
 package android.media.videoeditor;

 

 import java.io.IOException;

+import java.util.List;

 

 import android.graphics.Bitmap;

 import android.graphics.BitmapFactory;

@@ -24,9 +25,14 @@
 import android.graphics.Paint;

 import android.graphics.Rect;

 import android.util.Log;

+import android.util.Pair;

 

 /**

- * This class represents an image item on the storyboard.

+ * This class represents an image item on the storyboard. Note that images are

+ * scaled down to the maximum supported resolution by preserving the native

+ * aspect ratio. To learn the scaled image dimensions use

+ * {@link #getScaledWidth()} and {@link #getScaledHeight()} respectively.

+ *

  * {@hide}

  */

 public class MediaImageItem extends MediaItem {

@@ -41,6 +47,7 @@
     private final int mHeight;

     private final int mAspectRatio;

     private long mDurationMs;

+    private int mScaledWidth, mScaledHeight;

 

     /**

      * This class cannot be instantiated by using the default constructor

@@ -53,7 +60,7 @@
     /**

      * Constructor

      *

-     * @param mediaItemId The MediaItem id

+     * @param mediaItemId The media item id

      * @param filename The image file name

      * @param durationMs The duration of the image on the storyboard

      * @param renderingMode The rendering mode

@@ -64,7 +71,7 @@
             throws IOException {

         super(mediaItemId, filename, renderingMode);

 

-        // Determine the size of the image

+        // Determine the dimensions of the image

         final BitmapFactory.Options dbo = new BitmapFactory.Options();

         dbo.inJustDecodeBounds = true;

         BitmapFactory.decodeFile(filename, dbo);

@@ -75,6 +82,22 @@
 

         // TODO: Determine the aspect ratio from the width and height

         mAspectRatio = MediaProperties.ASPECT_RATIO_4_3;

+

+        // Images are stored in memory scaled to the maximum resolution to

+        // save memory.

+        final Pair<Integer, Integer>[] resolutions =

+            MediaProperties.getSupportedResolutions(mAspectRatio);

+        // Get the highest resolution

+        final Pair<Integer, Integer> maxResolution = resolutions[resolutions.length - 1];

+        if (mHeight > maxResolution.second) {

+            // We need to scale the image

+            scaleImage(filename, maxResolution.first, maxResolution.second);

+            mScaledWidth = maxResolution.first;

+            mScaledHeight = maxResolution.second;

+        } else {

+            mScaledWidth = mWidth;

+            mScaledHeight = mHeight;

+        }

     }

 

     /*

@@ -107,6 +130,20 @@
         return mHeight;

     }

 

+    /**

+     * @return The scaled width of the image.

+     */

+    public int getScaledWidth() {

+        return mScaledWidth;

+    }

+

+    /**

+     * @return The scaled height of the image.

+     */

+    public int getScaledHeight() {

+        return mScaledHeight;

+    }

+

     /*

      * {@inheritDoc}

      */

@@ -140,15 +177,31 @@
             }

         }

 

-        // TODO: Validate/modify the start and the end time of effects and overlays

-    }

+        final List<Overlay> overlays = getAllOverlays();

+        for (Overlay overlay : overlays) {

+            // Adjust the start time if necessary

+            if (overlay.getStartTime() < getTimelineDuration()) {

+                overlay.setStartTime(0);

+            }

 

-    /*

-     * {@inheritDoc}

-     */

-    @Override

-    public long getDuration() {

-        return mDurationMs;

+            // Adjust the duration if necessary

+            if (overlay.getStartTime() + overlay.getDuration() > getTimelineDuration()) {

+                overlay.setDuration(getTimelineDuration() - overlay.getStartTime());

+            }

+        }

+

+        final List<Effect> effects = getAllEffects();

+        for (Effect effect : effects) {

+            // Adjust the start time if necessary

+            if (effect.getStartTime() < getTimelineDuration()) {

+                effect.setStartTime(0);

+            }

+

+            // Adjust the duration if necessary

+            if (effect.getStartTime() + effect.getDuration() > getTimelineDuration()) {

+                effect.setDuration(getTimelineDuration() - effect.getStartTime());

+            }

+        }

     }

 

     /*

@@ -164,7 +217,7 @@
      */

     @Override

     public Bitmap getThumbnail(int width, int height, long timeMs) throws IOException {

-        return generateImageThumbnail(mFilename, width, height);

+        return scaleImage(mFilename, width, height);

     }

 

     /*

@@ -173,7 +226,7 @@
     @Override

     public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,

             int thumbnailCount) throws IOException {

-        final Bitmap thumbnail = generateImageThumbnail(mFilename, width, height);

+        final Bitmap thumbnail = scaleImage(mFilename, width, height);

         final Bitmap[] thumbnailArray = new Bitmap[thumbnailCount];

         for (int i = 0; i < thumbnailCount; i++) {

             thumbnailArray[i] = thumbnail;

@@ -182,7 +235,7 @@
     }

 

     /**

-     * Resize a bitmap within an input stream

+     * Resize a bitmap to the specified width and height

      *

      * @param filename The filename

      * @param width The thumbnail width

@@ -190,7 +243,7 @@
      *

      * @return The resized bitmap

      */

-    private Bitmap generateImageThumbnail(String filename, int width, int height)

+    private Bitmap scaleImage(String filename, int width, int height)

             throws IOException {

         final BitmapFactory.Options dbo = new BitmapFactory.Options();

         dbo.inJustDecodeBounds = true;

diff --git a/media/java/android/media/videoeditor/MediaItem.java b/media/java/android/media/videoeditor/MediaItem.java
index 12fbe540..f4651af 100755
--- a/media/java/android/media/videoeditor/MediaItem.java
+++ b/media/java/android/media/videoeditor/MediaItem.java
@@ -158,11 +158,6 @@
     }

 

     /**

-     * @return The duration of the media item

-     */

-    public abstract long getDuration();

-

-    /**

      * @return The timeline duration. This is the actual duration in the

      *      timeline (trimmed duration)

      */

@@ -210,11 +205,15 @@
      *      added.

      */

     public void addEffect(Effect effect) {

+        if (effect.getMediaItem() != this) {

+            throw new IllegalArgumentException("Media item mismatch");

+        }

+

         if (mEffects.contains(effect)) {

             throw new IllegalArgumentException("Effect already exists: " + effect.getId());

         }

 

-        if (effect.getStartTime() + effect.getDuration() > getDuration()) {

+        if (effect.getStartTime() + effect.getDuration() > getTimelineDuration()) {

             throw new IllegalArgumentException(

                     "Effect start time + effect duration > media clip duration");

         }

@@ -283,11 +282,15 @@
      *             the bitmap do not match the dimensions of the media item

      */

     public void addOverlay(Overlay overlay) {

+        if (overlay.getMediaItem() != this) {

+            throw new IllegalArgumentException("Media item mismatch");

+        }

+

         if (mOverlays.contains(overlay)) {

             throw new IllegalArgumentException("Overlay already exists: " + overlay.getId());

         }

 

-        if (overlay.getStartTime() + overlay.getDuration() > getDuration()) {

+        if (overlay.getStartTime() + overlay.getDuration() > getTimelineDuration()) {

             throw new IllegalArgumentException(

                     "Overlay start time + overlay duration > media clip duration");

         }

@@ -299,9 +302,18 @@
                 throw new IllegalArgumentException("Overlay bitmap not specified");

             }

 

+            final int scaledWidth, scaledHeight;

+            if (this instanceof MediaVideoItem) {

+                scaledWidth = getWidth();

+                scaledHeight = getHeight();

+            } else {

+                scaledWidth = ((MediaImageItem)this).getScaledWidth();

+                scaledHeight = ((MediaImageItem)this).getScaledHeight();

+            }

+

             // The dimensions of the overlay bitmap must be the same as the

             // media item dimensions

-            if (bitmap.getWidth() != getWidth() || bitmap.getHeight() != getHeight()) {

+            if (bitmap.getWidth() != scaledWidth || bitmap.getHeight() != scaledHeight) {

                 throw new IllegalArgumentException(

                         "Bitmap dimensions must match media item dimensions");

             }

@@ -424,7 +436,7 @@
      *

      * @param effect The effect that was added or removed

      */

-    private void invalidateTransitions(Effect effect) {

+    void invalidateTransitions(Effect effect) {

         // Check if the effect overlaps with the beginning and end transitions

         if (mBeginTransition != null) {

             if (effect.getStartTime() < mBeginTransition.getDuration()) {

@@ -433,7 +445,7 @@
         }

 

         if (mEndTransition != null) {

-            if (effect.getStartTime() + effect.getDuration() > getDuration()

+            if (effect.getStartTime() + effect.getDuration() > getTimelineDuration()

                     - mEndTransition.getDuration()) {

                 mEndTransition.invalidate();

             }

@@ -445,7 +457,7 @@
      *

      * @param overlay The effect that was added or removed

      */

-    private void invalidateTransitions(Overlay overlay) {

+    void invalidateTransitions(Overlay overlay) {

         // Check if the overlay overlaps with the beginning and end transitions

         if (mBeginTransition != null) {

             if (overlay.getStartTime() < mBeginTransition.getDuration()) {

@@ -454,7 +466,7 @@
         }

 

         if (mEndTransition != null) {

-            if (overlay.getStartTime() + overlay.getDuration() > getDuration()

+            if (overlay.getStartTime() + overlay.getDuration() > getTimelineDuration()

                     - mEndTransition.getDuration()) {

                 mEndTransition.invalidate();

             }

diff --git a/media/java/android/media/videoeditor/MediaProperties.java b/media/java/android/media/videoeditor/MediaProperties.java
index c3f5ef7..34088fc 100755
--- a/media/java/android/media/videoeditor/MediaProperties.java
+++ b/media/java/android/media/videoeditor/MediaProperties.java
@@ -29,6 +29,7 @@
     public static final int HEIGHT_360 = 360;

     public static final int HEIGHT_480 = 480;

     public static final int HEIGHT_720 = 720;

+    public static final int HEIGHT_1080 = 1080;

 

     // Supported aspect ratios

     public static final int ASPECT_RATIO_UNDEFINED = 0;

diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index afca55c..47d4fa0 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -17,6 +17,7 @@
 package android.media.videoeditor;

 

 import java.io.IOException;

+import java.util.List;

 

 import android.graphics.Bitmap;

 import android.media.MediaRecorder;

@@ -81,7 +82,8 @@
         private final PlaybackProgressListener mListener;

         private final int mCallbackAfterFrameCount;

         private final long mFromMs, mToMs;

-        private boolean mRun, mLoop;

+        private boolean mRun;

+        private final boolean mLoop;

         private long mPositionMs;

 

         /**

@@ -222,8 +224,8 @@
             String audioWaveformFilename)  throws IOException {

         super(mediaItemId, filename, renderingMode);

         // TODO: Set these variables correctly

-        mWidth = 0;

-        mHeight = 0;

+        mWidth = 1080;

+        mHeight = 720;

         mAspectRatio = MediaProperties.ASPECT_RATIO_3_2;

         mFileType = MediaProperties.FILE_MP4;

         mVideoType = MediaRecorder.VideoEncoder.H264;

@@ -297,7 +299,31 @@
             }

         }

 

-        // TODO: Validate/modify the start and the end time of effects and overlays

+        final List<Overlay> overlays = getAllOverlays();

+        for (Overlay overlay : overlays) {

+            // Adjust the start time if necessary

+            if (overlay.getStartTime() < mBeginBoundaryTimeMs) {

+                overlay.setStartTime(mBeginBoundaryTimeMs);

+            }

+

+            // Adjust the duration if necessary

+            if (overlay.getStartTime() + overlay.getDuration() > getTimelineDuration()) {

+                overlay.setDuration(getTimelineDuration() - overlay.getStartTime());

+            }

+        }

+

+        final List<Effect> effects = getAllEffects();

+        for (Effect effect : effects) {

+            // Adjust the start time if necessary

+            if (effect.getStartTime() < mBeginBoundaryTimeMs) {

+                effect.setStartTime(mBeginBoundaryTimeMs);

+            }

+

+            // Adjust the duration if necessary

+            if (effect.getStartTime() + effect.getDuration() > getTimelineDuration()) {

+                effect.setDuration(getTimelineDuration() - effect.getStartTime());

+            }

+        }

     }

 

     /**

@@ -374,10 +400,9 @@
         return mHeight;

     }

 

-    /*

-     * {@inheritDoc}

+    /**

+     * @return The duration of the video clip

      */

-    @Override

     public long getDuration() {

         return mDurationMs;

     }

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

 package android.media.videoeditor;

 

+import java.util.HashMap;

+import java.util.Map;

+

 

 /**

  * This is the super class for all Overlay classes.

@@ -24,23 +27,27 @@
 public abstract class Overlay {

     // Instance variables

     private final String mUniqueId;

+    // The overlay owner

+    private final MediaItem mMediaItem;

+    // user attributes

+    private final Map<String, String> mUserAttributes;

 

     protected long mStartTimeMs;

     protected long mDurationMs;

 

+

     /**

      * Default constructor

      */

     @SuppressWarnings("unused")

     private Overlay() {

-        mUniqueId = null;

-        mStartTimeMs = 0;

-        mDurationMs = 0;

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

     }

 

     /**

      * Constructor

      *

+     * @param mediaItem The media item owner

      * @param overlayId The overlay id

      * @param startTimeMs The start time relative to the media item start time

      * @param durationMs The duration

@@ -48,10 +55,20 @@
      * @throws IllegalArgumentException if the file type is not PNG or the

      *      startTimeMs and durationMs are incorrect.

      */

-    public Overlay(String overlayId, long startTimeMs, long durationMs) {

+    public Overlay(MediaItem mediaItem, String overlayId, long startTimeMs, long durationMs) {

+        if (mediaItem == null) {

+            throw new IllegalArgumentException("Media item cannot be null");

+        }

+

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

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

+        }

+

+        mMediaItem = mediaItem;

         mUniqueId = overlayId;

         mStartTimeMs = startTimeMs;

         mDurationMs = durationMs;

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

     }

 

     /**

@@ -75,7 +92,13 @@
      * @param durationMs The duration in milliseconds

      */

     public void setDuration(long durationMs) {

+        if (mStartTimeMs + durationMs > mMediaItem.getTimelineDuration()) {

+            throw new IllegalArgumentException("Duration is too large");

+        }

+

         mDurationMs = durationMs;

+

+        mMediaItem.invalidateTransitions(this);

     }

 

     /**

@@ -93,7 +116,37 @@
      * @param startTimeMs start time in milliseconds

      */

     public void setStartTime(long startTimeMs) {

+        if (startTimeMs + mDurationMs > mMediaItem.getTimelineDuration()) {

+            throw new IllegalArgumentException("Start time is too large");

+        }

+

         mStartTimeMs = startTimeMs;

+

+        mMediaItem.invalidateTransitions(this);

+    }

+

+    /**

+     * @return The media item owner

+     */

+    public MediaItem getMediaItem() {

+        return mMediaItem;

+    }

+

+    /**

+     * Set a user attribute

+     *

+     * @param name The attribute name

+     * @param value The attribute value

+     */

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

+        mUserAttributes.put(name, value);

+    }

+

+    /**

+     * @return The user attributes

+     */

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

+        return mUserAttributes;

     }

 

     /*

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

 public class OverlayFrame extends Overlay {

     // Instance variables

-    private final Bitmap mBitmap;

+    private Bitmap mBitmap;

     private String mFilename;

 

     /**

@@ -41,12 +41,13 @@
      */

     @SuppressWarnings("unused")

     private OverlayFrame() {

-        this(null, (String)null, 0, 0);

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

     }

 

     /**

      * Constructor for an OverlayFrame

      *

+     * @param mediaItem The media item owner

      * @param overlayId The overlay id

      * @param bitmap The bitmap to be used as an overlay. The size of the

      *      bitmap must equal to the size of the media item to which it is

@@ -57,9 +58,9 @@
      * @throws IllegalArgumentException if the file type is not PNG or the

      *      startTimeMs and durationMs are incorrect.

      */

-    public OverlayFrame(String overlayId, Bitmap bitmap, long startTimeMs,

+    public OverlayFrame(MediaItem mediaItem, String overlayId, Bitmap bitmap, long startTimeMs,

             long durationMs) {

-        super(overlayId, startTimeMs, durationMs);

+        super(mediaItem, overlayId, startTimeMs, durationMs);

         mBitmap = bitmap;

         mFilename = null;

     }

@@ -68,6 +69,7 @@
      * Constructor for an OverlayFrame. This constructor can be used to

      * restore the overlay after it was saved internally by the video editor.

      *

+     * @param mediaItem The media item owner

      * @param overlayId The overlay id

      * @param filename The file name that contains the overlay.

      * @param startTimeMs The overlay start time in milliseconds

@@ -76,8 +78,9 @@
      * @throws IllegalArgumentException if the file type is not PNG or the

      *      startTimeMs and durationMs are incorrect.

      */

-    OverlayFrame(String overlayId, String filename, long startTimeMs, long durationMs) {

-        super(overlayId, startTimeMs, durationMs);

+    OverlayFrame(MediaItem mediaItem, String overlayId, String filename, long startTimeMs,

+            long durationMs) {

+        super(mediaItem, overlayId, startTimeMs, durationMs);

         mFilename = filename;

         mBitmap = BitmapFactory.decodeFile(mFilename);

     }

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

 

     /**

+     * @param bitmap The overlay bitmap

+     */

+    public void setBitmap(Bitmap bitmap) {

+        mBitmap = bitmap;

+        if (mFilename != null) {

+            // Delete the file

+            new File(mFilename).delete();

+            // Invalidate the filename

+            mFilename = null;

+        }

+

+        // Invalidate the transitions if necessary

+        getMediaItem().invalidateTransitions(this);

+    }

+

+    /**

      * Get the file name of this overlay

      */

     String getFilename() {

diff --git a/media/java/android/media/videoeditor/Transition.java b/media/java/android/media/videoeditor/Transition.java
index e972aeb..1c82742 100755
--- a/media/java/android/media/videoeditor/Transition.java
+++ b/media/java/android/media/videoeditor/Transition.java
@@ -147,11 +147,12 @@
      */

     public long getMaximumDuration() {

         if (mAfterMediaItem == null) {

-            return mBeforeMediaItem.getDuration() / 2;

+            return mBeforeMediaItem.getTimelineDuration() / 2;

         } else if (mBeforeMediaItem == null) {

-            return mAfterMediaItem.getDuration() / 2;

+            return mAfterMediaItem.getTimelineDuration() / 2;

         } else {

-            return (Math.min(mAfterMediaItem.getDuration(), mBeforeMediaItem.getDuration()) / 2);

+            return (Math.min(mAfterMediaItem.getTimelineDuration(),

+                    mBeforeMediaItem.getTimelineDuration()) / 2);

         }

     }

 

diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
index 7226d5d..5df4ea5 100644
--- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -50,6 +51,10 @@
     private static final String TAG_MEDIA_ITEM = "media_item";
     private static final String TAG_TRANSITIONS = "transitions";
     private static final String TAG_TRANSITION = "transition";
+    private static final String TAG_OVERLAYS = "overlays";
+    private static final String TAG_OVERLAY = "overlay";
+    private static final String TAG_OVERLAY_USER_ATTRIBUTES = "overlay_user_attributes";
+
     private static final String ATTR_ID = "id";
     private static final String ATTR_FILENAME = "filename";
     private static final String ATTR_AUDIO_WAVEFORM_FILENAME = "wavefoem";
@@ -568,7 +573,42 @@
                             mvi.getAudioWaveformFilename());
                 }
             } else if (mediaItem instanceof MediaImageItem) {
-                serializer.attribute("", ATTR_DURATION, Long.toString(mediaItem.getDuration()));
+                serializer.attribute("", ATTR_DURATION,
+                        Long.toString(mediaItem.getTimelineDuration()));
+            }
+
+            final List<Overlay> overlays = mediaItem.getAllOverlays();
+            if (overlays.size() > 0) {
+                serializer.startTag("", TAG_OVERLAYS);
+                for (Overlay overlay : overlays) {
+                    serializer.startTag("", TAG_OVERLAY);
+                    serializer.attribute("", ATTR_ID, overlay.getId());
+                    serializer.attribute("", ATTR_TYPE, overlay.getClass().getSimpleName());
+                    serializer.attribute("", ATTR_BEGIN_TIME,
+                            Long.toString(overlay.getStartTime()));
+                    serializer.attribute("", ATTR_DURATION, Long.toString(overlay.getDuration()));
+                    if (overlay instanceof OverlayFrame) {
+                        final OverlayFrame overlayFrame = (OverlayFrame)overlay;
+                        overlayFrame.save(this);
+                        if (overlayFrame.getFilename() != null) {
+                            serializer.attribute("", ATTR_FILENAME, overlayFrame.getFilename());
+                        }
+                    }
+
+                    // Save the user attributes
+                    serializer.startTag("", TAG_OVERLAY_USER_ATTRIBUTES);
+                    final Map<String, String> userAttributes = overlay.getUserAttributes();
+                    for (String name : userAttributes.keySet()) {
+                        final String value = userAttributes.get(name);
+                        if (value != null) {
+                            serializer.attribute("", name, value);
+                        }
+                    }
+                    serializer.endTag("", TAG_OVERLAY_USER_ATTRIBUTES);
+
+                    serializer.endTag("", TAG_OVERLAY);
+                }
+                serializer.endTag("", TAG_OVERLAYS);
             }
 
             serializer.endTag("", TAG_MEDIA_ITEM);
@@ -628,21 +668,22 @@
         parser.setInput(new FileInputStream(file), "UTF-8");
         int eventType = parser.getEventType();
         String name;
+        MediaItem currentMediaItem = null;
+        Overlay currentOverlay = null;
         while (eventType != XmlPullParser.END_DOCUMENT) {
             switch (eventType) {
                 case XmlPullParser.START_TAG: {
                     name = parser.getName();
-                    if (name.equals(TAG_PROJECT)) {
+                    if (TAG_PROJECT.equals(name)) {
                         mAspectRatio = Integer.parseInt(parser.getAttributeValue("",
                                 ATTR_ASPECT_RATIO));
-                    } else if (name.equals(TAG_MEDIA_ITEM)) {
+                    } else if (TAG_MEDIA_ITEM.equals(name)) {
                         final String mediaItemId = parser.getAttributeValue("", ATTR_ID);
                         final String type = parser.getAttributeValue("", ATTR_TYPE);
                         final String filename = parser.getAttributeValue("", ATTR_FILENAME);
                         final int renderingMode = Integer.parseInt(parser.getAttributeValue("",
                                 ATTR_RENDERING_MODE));
 
-                        MediaItem currentMediaItem;
                         if (MediaImageItem.class.getSimpleName().equals(type)) {
                             final long durationMs = Long.parseLong(parser.getAttributeValue("",
                                     ATTR_DURATION));
@@ -672,11 +713,36 @@
                         if (currentMediaItem != null) {
                             mMediaItems.add(currentMediaItem);
                         }
-                    } else if (name.equals(TAG_TRANSITION)) {
+                    } else if (TAG_TRANSITION.equals(name)) {
                         final Transition transition = parseTransition(parser);
                         if (transition != null) {
                             mTransitions.add(transition);
                         }
+                    } else if (TAG_OVERLAY.equals(name)) {
+                        if (currentMediaItem != null) {
+                            currentOverlay = parseOverlay(parser, currentMediaItem);
+                            if (currentOverlay != null) {
+                                currentMediaItem.addOverlay(currentOverlay);
+                            }
+                        }
+                    } else if (TAG_OVERLAY_USER_ATTRIBUTES.equals(name)) {
+                        if (currentOverlay != null) {
+                            final int attributesCount = parser.getAttributeCount();
+                            for (int i = 0; i < attributesCount; i++) {
+                                currentOverlay.setUserAttribute(parser.getAttributeName(i),
+                                        parser.getAttributeValue(i));
+                            }
+                        }
+                    }
+                    break;
+                }
+
+                case XmlPullParser.END_TAG: {
+                    name = parser.getName();
+                    if (TAG_MEDIA_ITEM.equals(name)) {
+                        currentMediaItem = null;
+                    } else if (TAG_OVERLAY.equals(name)) {
+                        currentOverlay = null;
                     }
                     break;
                 }
@@ -763,6 +829,31 @@
         return transition;
     }
 
+    /**
+     * Parse the overlay
+     *
+     * @param parser The parser
+     * @param mediaItem The media item owner
+     *
+     * @return The overlay
+     */
+    private Overlay parseOverlay(XmlPullParser parser, MediaItem mediaItem) {
+        final String overlayId = parser.getAttributeValue("", ATTR_ID);
+        final String type = parser.getAttributeValue("", ATTR_TYPE);
+        final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION));
+        final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME));
+
+        final Overlay overlay;
+        if (OverlayFrame.class.getSimpleName().equals(type)) {
+            final String filename = parser.getAttributeValue("", ATTR_FILENAME);
+            overlay = new OverlayFrame(mediaItem, overlayId, filename, startTimeMs, durationMs);
+        } else {
+            overlay = null;
+        }
+
+        return overlay;
+    }
+
     public void cancelExport(String filename) {
     }
 
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index b5f4856..d6bf609 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -207,10 +207,13 @@
                                             uint64_t size,
                                             time_t modified) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring pathStr = env->NewStringUTF(path);
     MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject,
-            env->NewStringUTF(path), (jint)format, (jint)parent, (jint)storage,
+            pathStr, (jint)format, (jint)parent, (jint)storage,
             (jlong)size, (jlong)modified);
 
+    if (pathStr)
+        env->DeleteLocalRef(pathStr);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return result;
 }
@@ -218,9 +221,12 @@
 void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
                                 MtpObjectFormat format, bool succeeded) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    env->CallVoidMethod(mDatabase, method_endSendObject, env->NewStringUTF(path),
+    jstring pathStr = env->NewStringUTF(path);
+    env->CallVoidMethod(mDatabase, method_endSendObject, pathStr,
                         (jint)handle, (jint)format, (jboolean)succeeded);
 
+    if (pathStr)
+        env->DeleteLocalRef(pathStr);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
@@ -238,6 +244,7 @@
     for (int i = 0; i < length; i++)
         list->push(handles[i]);
     env->ReleaseIntArrayElements(array, handles, 0);
+    env->DeleteLocalRef(array);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return list;
@@ -266,6 +273,7 @@
     for (int i = 0; i < length; i++)
         list->push(formats[i]);
     env->ReleaseIntArrayElements(array, formats, 0);
+    env->DeleteLocalRef(array);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return list;
@@ -283,6 +291,7 @@
     for (int i = 0; i < length; i++)
         list->push(formats[i]);
     env->ReleaseIntArrayElements(array, formats, 0);
+    env->DeleteLocalRef(array);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return list;
@@ -300,6 +309,7 @@
     for (int i = 0; i < length; i++)
         list->push(properties[i]);
     env->ReleaseIntArrayElements(array, properties, 0);
+    env->DeleteLocalRef(array);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return list;
@@ -317,6 +327,7 @@
     for (int i = 0; i < length; i++)
         list->push(properties[i]);
     env->ReleaseIntArrayElements(array, properties, 0);
+    env->DeleteLocalRef(array);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return list;
@@ -342,14 +353,21 @@
     jlong longValue = longValues[0];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
-    // special case MTP_PROPERTY_DATE_MODIFIED, which is a string to MTP
+    // special case date properties, which are strings to MTP
     // but stored internally as a uint64
-    if (property == MTP_PROPERTY_DATE_MODIFIED) {
+    if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
         char    date[20];
         formatDateTime(longValue, date, sizeof(date));
         packet.putString(date);
         return MTP_RESPONSE_OK;
     }
+    // release date is stored internally as just the year
+    if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
+        char    date[20];
+        snprintf(date, sizeof(date), "%04lld0101T000000", longValue);
+        packet.putString(date);
+        return MTP_RESPONSE_OK;
+    }
 
     switch (type) {
         case MTP_TYPE_INT8:
@@ -449,6 +467,8 @@
 
     jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
                 (jint)handle, (jint)property, longValue, stringValue);
+    if (stringValue)
+        env->DeleteLocalRef(stringValue);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return result;
@@ -570,6 +590,8 @@
 
     jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
                 (jint)property, longValue, stringValue);
+    if (stringValue)
+        env->DeleteLocalRef(stringValue);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return result;
@@ -680,6 +702,17 @@
     {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
     {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
     {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
+    {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
+    {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
+    {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
+    {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
+    {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
+    {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
+    {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
+    {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
 };
 
 static const PropertyTableEntry   kDevicePropertyTable[] = {
@@ -723,6 +756,7 @@
     for (int i = 0; i < length; i++)
         list->push(handles[i]);
     env->ReleaseIntArrayElements(array, handles, 0);
+    env->DeleteLocalRef(array);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return list;
@@ -743,6 +777,7 @@
     env->ReleaseIntArrayElements(array, handles, 0);
     MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
                 (jint)handle, array);
+    env->DeleteLocalRef(array);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return result;
@@ -754,10 +789,12 @@
     switch (property) {
         case MTP_PROPERTY_OBJECT_FORMAT:
         case MTP_PROPERTY_PROTECTION_STATUS:
+        case MTP_PROPERTY_TRACK:
             result = new MtpProperty(property, MTP_TYPE_UINT16);
             break;
         case MTP_PROPERTY_STORAGE_ID:
         case MTP_PROPERTY_PARENT_OBJECT:
+        case MTP_PROPERTY_DURATION:
             result = new MtpProperty(property, MTP_TYPE_UINT32);
             break;
         case MTP_PROPERTY_OBJECT_SIZE:
@@ -769,6 +806,15 @@
         case MTP_PROPERTY_NAME:
         case MTP_PROPERTY_OBJECT_FILE_NAME:
         case MTP_PROPERTY_DATE_MODIFIED:
+        case MTP_PROPERTY_DISPLAY_NAME:
+        case MTP_PROPERTY_DATE_ADDED:
+        case MTP_PROPERTY_ARTIST:
+        case MTP_PROPERTY_ALBUM_NAME:
+        case MTP_PROPERTY_ALBUM_ARTIST:
+        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+        case MTP_PROPERTY_GENRE:
+        case MTP_PROPERTY_COMPOSER:
+        case MTP_PROPERTY_DESCRIPTION:
             result = new MtpProperty(property, MTP_TYPE_STR);
             break;
     }
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 8f40130..ebe3302 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -63,7 +63,6 @@
 
 // Flag to allow a one time init of global memory, only happens on first call ever
 int LvmInitFlag = LVM_FALSE;
-int LvmSessionsActive = 0;
 SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
 
 int SessionIndex[LVM_MAX_SESSIONS];
@@ -189,16 +188,19 @@
                             int32_t             sessionId,
                             int32_t             ioId,
                             effect_interface_t  *pInterface){
-    int ret;
+    int ret = 0;
     int sessionNo;
     int i;
-    EffectContext *pContext = new EffectContext;
+    EffectContext *pContext = NULL;
+    bool newBundle = false;
+    SessionContext *pSessionContext;
 
     LOGV("\n\tEffectCreate start session %d", sessionId);
 
     if (pInterface == NULL || uuid == NULL){
         LOGV("\tLVM_ERROR : EffectCreate() called with NULL pointer");
-        return -EINVAL;
+        ret = -EINVAL;
+        goto exit;
     }
 
     if(LvmInitFlag == LVM_FALSE){
@@ -207,8 +209,6 @@
         LvmGlobalBundle_init();
     }
 
-    LOGV("\tEffectCreate: There are %d LVM sessions acive\n", LvmSessionsActive);
-
     // Find next available sessionNo
     for(i=0; i<LVM_MAX_SESSIONS; i++){
         if((SessionIndex[i] == LVM_UNUSED_SESSION)||(SessionIndex[i] == sessionId)){
@@ -221,23 +221,20 @@
 
     if(i==LVM_MAX_SESSIONS){
         LOGV("\tLVM_ERROR : Cannot find memory to allocate for current session");
-        return -EINVAL;
+        ret = -EINVAL;
+        goto exit;
     }
+
+    pContext = new EffectContext;
+
     // If this is the first create in this session
     if(GlobalSessionMemory[sessionNo].bBundledEffectsEnabled == LVM_FALSE){
         LOGV("\tEffectCreate - This is the first effect in current sessionId %d sessionNo %d",
                 sessionId, sessionNo);
 
-        LvmSessionsActive++;
-
-        if(LvmSessionsActive >= LVM_MAX_SESSIONS){
-            LOGV("\tLVM_ERROR : Number of active session is greater than LVM_MAX_SESSIONS (%d)",
-                  LVM_MAX_SESSIONS);
-            return -EINVAL;
-        }
-
         GlobalSessionMemory[sessionNo].bBundledEffectsEnabled = LVM_TRUE;
         GlobalSessionMemory[sessionNo].pBundledContext        = new BundledEffectContext;
+        newBundle = true;
 
         pContext->pBundledContext = GlobalSessionMemory[sessionNo].pBundledContext;
         pContext->pBundledContext->SessionNo                = sessionNo;
@@ -251,17 +248,16 @@
         pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE;
         pContext->pBundledContext->NumberEffectsEnabled     = 0;
         pContext->pBundledContext->NumberEffectsCalled      = 0;
-        pContext->pBundledContext->frameCount               = 0;
         pContext->pBundledContext->firstVolume              = LVM_TRUE;
 
         #ifdef LVM_PCM
-
         char fileName[256];
         snprintf(fileName, 256, "/data/tmp/bundle_%p_pcm_in.pcm", pContext->pBundledContext);
         pContext->pBundledContext->PcmInPtr = fopen(fileName, "w");
         if (pContext->pBundledContext->PcmInPtr == NULL) {
             LOGV("cannot open %s", fileName);
-           return -EINVAL;
+            ret = -EINVAL;
+            goto exit;
         }
 
         snprintf(fileName, 256, "/data/tmp/bundle_%p_pcm_out.pcm", pContext->pBundledContext);
@@ -270,7 +266,8 @@
             LOGV("cannot open %s", fileName);
             fclose(pContext->pBundledContext->PcmInPtr);
            pContext->pBundledContext->PcmInPtr = NULL;
-           return -EINVAL;
+           ret = -EINVAL;
+           goto exit;
         }
         #endif
 
@@ -285,15 +282,18 @@
         pContext->pBundledContext->bMuteEnabled             = LVM_FALSE;
         pContext->pBundledContext->bStereoPositionEnabled   = LVM_FALSE;
         pContext->pBundledContext->positionSaved            = 0;
+        pContext->pBundledContext->workBuffer               = NULL;
+        pContext->pBundledContext->frameCount               = -1;
+        pContext->pBundledContext->SamplesToExitCountVirt   = 0;
+        pContext->pBundledContext->SamplesToExitCountBb     = 0;
+        pContext->pBundledContext->SamplesToExitCountEq     = 0;
 
         LOGV("\tEffectCreate - Calling LvmBundle_init");
         ret = LvmBundle_init(pContext);
 
         if (ret < 0){
             LOGV("\tLVM_ERROR : EffectCreate() Bundle init failed");
-            delete pContext->pBundledContext;
-            delete pContext;
-            return ret;
+            goto exit;
         }
     }
     else{
@@ -304,13 +304,14 @@
     }
     LOGV("\tEffectCreate - pBundledContext is %p", pContext->pBundledContext);
 
-    SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
+    pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
 
     // Create each Effect
     if (memcmp(uuid, &gBassBoostDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
         // Create Bass Boost
         LOGV("\tEffectCreate - Effect to be created is LVM_BASS_BOOST");
         pSessionContext->bBassInstantiated = LVM_TRUE;
+        pContext->pBundledContext->SamplesToExitCountBb = 0;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_BASS_BOOST;
@@ -318,6 +319,7 @@
         // Create Virtualizer
         LOGV("\tEffectCreate - Effect to be created is LVM_VIRTUALIZER");
         pSessionContext->bVirtualizerInstantiated=LVM_TRUE;
+        pContext->pBundledContext->SamplesToExitCountVirt = 0;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_VIRTUALIZER;
@@ -325,6 +327,7 @@
         // Create Equalizer
         LOGV("\tEffectCreate - Effect to be created is LVM_EQUALIZER");
         pSessionContext->bEqualizerInstantiated = LVM_TRUE;
+        pContext->pBundledContext->SamplesToExitCountEq = 0;
 
         pContext->itfe       = &gLvmEffectInterface;
         pContext->EffectType = LVM_EQUALIZER;
@@ -338,46 +341,77 @@
     }
     else{
         LOGV("\tLVM_ERROR : EffectCreate() invalid UUID");
-        return -EINVAL;
+        ret = -EINVAL;
+        goto exit;
     }
 
-    *pInterface = (effect_interface_t)pContext;
+exit:
+    if (ret != 0) {
+        if (pContext != NULL) {
+            if (newBundle) {
+                GlobalSessionMemory[sessionNo].bBundledEffectsEnabled = LVM_FALSE;
+                SessionIndex[sessionNo] = LVM_UNUSED_SESSION;
+                delete pContext->pBundledContext;
+            }
+            delete pContext;
+        }
+        *pInterface = (effect_interface_t)NULL;
+    } else {
+        *pInterface = (effect_interface_t)pContext;
+    }
     LOGV("\tEffectCreate end..\n\n");
-    return 0;
+    return ret;
 } /* end EffectCreate */
 
 extern "C" int EffectRelease(effect_interface_t interface){
     LOGV("\n\tEffectRelease start %p", interface);
     EffectContext * pContext = (EffectContext *)interface;
 
-    LOGV("\n\tEffectRelease start interface: %p, context %p", interface, pContext->pBundledContext);
+    LOGV("\tEffectRelease start interface: %p, context %p", interface, pContext->pBundledContext);
     if (pContext == NULL){
         LOGV("\tLVM_ERROR : EffectRelease called with NULL pointer");
         return -EINVAL;
     }
 
-
-    Effect_setEnabled(pContext, LVM_FALSE);
-
     SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
 
     // Clear the instantiated flag for the effect
+    // protect agains the case where an effect is un-instantiated without being disabled
     if(pContext->EffectType == LVM_BASS_BOOST) {
         LOGV("\tEffectRelease LVM_BASS_BOOST Clearing global intstantiated flag");
         pSessionContext->bBassInstantiated = LVM_FALSE;
+        if(pContext->pBundledContext->SamplesToExitCountBb > 0){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
+        pContext->pBundledContext->SamplesToExitCountBb = 0;
     } else if(pContext->EffectType == LVM_VIRTUALIZER) {
         LOGV("\tEffectRelease LVM_VIRTUALIZER Clearing global intstantiated flag");
         pSessionContext->bVirtualizerInstantiated = LVM_FALSE;
+        if(pContext->pBundledContext->SamplesToExitCountVirt > 0){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
+        pContext->pBundledContext->SamplesToExitCountVirt = 0;
     } else if(pContext->EffectType == LVM_EQUALIZER) {
         LOGV("\tEffectRelease LVM_EQUALIZER Clearing global intstantiated flag");
         pSessionContext->bEqualizerInstantiated =LVM_FALSE;
+        if(pContext->pBundledContext->SamplesToExitCountEq > 0){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
+        pContext->pBundledContext->SamplesToExitCountEq = 0;
     } else if(pContext->EffectType == LVM_VOLUME) {
         LOGV("\tEffectRelease LVM_VOLUME Clearing global intstantiated flag");
         pSessionContext->bVolumeInstantiated = LVM_FALSE;
+        if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE){
+            pContext->pBundledContext->NumberEffectsEnabled--;
+        }
     } else {
         LOGV("\tLVM_ERROR : EffectRelease : Unsupported effect\n\n\n\n\n\n\n");
     }
 
+    // Disable effect, in this case ignore errors (return codes)
+    // if an effect has already been disabled
+    Effect_setEnabled(pContext, LVM_FALSE);
+
     // if all effects are no longer instantiaed free the lvm memory and delete BundledEffectContext
     if ((pSessionContext->bBassInstantiated == LVM_FALSE) &&
             (pSessionContext->bVolumeInstantiated == LVM_FALSE) &&
@@ -395,8 +429,6 @@
         }
         #endif
 
-        LvmSessionsActive--;
-        LOGV("\tEffectRelease: There are %d LVM sessions remaining\n", LvmSessionsActive);
 
         // Clear the SessionIndex
         for(int i=0; i<LVM_MAX_SESSIONS; i++){
@@ -409,11 +441,14 @@
         }
 
         LOGV("\tEffectRelease: All effects are no longer instantiated\n");
-        pSessionContext->bBundledEffectsEnabled =LVM_FALSE;
+        pSessionContext->bBundledEffectsEnabled = LVM_FALSE;
         pSessionContext->pBundledContext = LVM_NULL;
         LOGV("\tEffectRelease: Freeing LVM Bundle memory\n");
         LvmEffect_free(pContext);
         LOGV("\tEffectRelease: Deleting LVM Bundle context %p\n", pContext->pBundledContext);
+        if (pContext->pBundledContext->workBuffer != NULL) {
+            free(pContext->pBundledContext->workBuffer);
+        }
         delete pContext->pBundledContext;
         pContext->pBundledContext = LVM_NULL;
     }
@@ -643,6 +678,14 @@
     return 0;
 }   /* end LvmBundle_init */
 
+
+static inline int16_t clamp16(int32_t sample)
+{
+    if ((sample>>15) ^ (sample>>31))
+        sample = 0x7FFF ^ (sample>>31);
+    return sample;
+}
+
 //----------------------------------------------------------------------------
 // LvmBundle_process()
 //----------------------------------------------------------------------------
@@ -668,39 +711,25 @@
 
     LVM_ControlParams_t     ActiveParams;                           /* Current control Parameters */
     LVM_ReturnStatus_en     LvmStatus = LVM_SUCCESS;                /* Function call status */
-
     LVM_INT16               *pOutTmp;
+
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE){
         pOutTmp = pOut;
     }else if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
-        pOutTmp = (LVM_INT16 *)malloc(frameCount * sizeof(LVM_INT16) * 2);
-        if(pOutTmp == NULL){
-            LOGV("\tLVM_ERROR : LvmBundle_process failed to allocate memory for "
-            "EFFECT_BUFFER_ACCESS_ACCUMULATE mode");
-            return -EINVAL;
+        if (pContext->pBundledContext->frameCount != frameCount) {
+            if (pContext->pBundledContext->workBuffer != NULL) {
+                free(pContext->pBundledContext->workBuffer);
+            }
+            pContext->pBundledContext->workBuffer =
+                    (LVM_INT16 *)malloc(frameCount * sizeof(LVM_INT16) * 2);
+            pContext->pBundledContext->frameCount = frameCount;
         }
+        pOutTmp = pContext->pBundledContext->workBuffer;
     }else{
         LOGV("LVM_ERROR : LvmBundle_process invalid access mode");
         return -EINVAL;
     }
 
-    /* Get the current settings */
-    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance,
-                                         &ActiveParams);
-
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "LvmBundle_process")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-
-    pContext->pBundledContext->frameCount++;
-    if(pContext->pBundledContext->frameCount == 100)
-    {
-        //LOGV("\tBB: %d VIRT: %d EQ: %d, session (%d), context is %p\n",
-        //ActiveParams.BE_OperatingMode,
-        //ActiveParams.VirtualizerOperatingMode, ActiveParams.EQNB_OperatingMode,
-        //pContext->pBundledContext->SessionNo, pContext->pBundledContext);
-        pContext->pBundledContext->frameCount = 0;
-    }
-
     #ifdef LVM_PCM
     fwrite(pIn, frameCount*sizeof(LVM_INT16)*2, 1, pContext->pBundledContext->PcmInPtr);
     fflush(pContext->pBundledContext->PcmInPtr);
@@ -725,9 +754,8 @@
 
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
         for (int i=0; i<frameCount*2; i++){
-            pOut[i] +=  pOutTmp[i];
+            pOut[i] = clamp16((LVM_INT32)pOut[i] + (LVM_INT32)pOutTmp[i]);
         }
-        free(pOutTmp);
     }
     return 0;
 }    /* end LvmBundle_process */
@@ -813,15 +841,15 @@
         ActiveParams.BE_OperatingMode       = LVM_BE_OFF;
     }
     if(pContext->EffectType == LVM_VIRTUALIZER) {
-        LOGV("\tLvmEffect_disable : Enabling LVM_VIRTUALIZER");
+        LOGV("\tLvmEffect_disable : Disabling LVM_VIRTUALIZER");
         ActiveParams.VirtualizerOperatingMode   = LVM_MODE_OFF;
     }
     if(pContext->EffectType == LVM_EQUALIZER) {
-        LOGV("\tLvmEffect_disable : Enabling LVM_EQUALIZER");
+        LOGV("\tLvmEffect_disable : Disabling LVM_EQUALIZER");
         ActiveParams.EQNB_OperatingMode     = LVM_EQNB_OFF;
     }
     if(pContext->EffectType == LVM_VOLUME) {
-        LOGV("\tLvmEffect_disable : Enabling LVM_VOLUME");
+        LOGV("\tLvmEffect_disable : Disabling LVM_VOLUME");
     }
 
     LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
@@ -2406,85 +2434,114 @@
         switch (pContext->EffectType) {
             case LVM_BASS_BOOST:
                 if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
-                     LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already enabled");
+                     LOGV("\tEffect_setEnabled() LVM_BASS_BOOST is already enabled");
                      return -EINVAL;
                 }
+                if(pContext->pBundledContext->SamplesToExitCountBb <= 0){
+                    pContext->pBundledContext->NumberEffectsEnabled++;
+                }
                 pContext->pBundledContext->SamplesToExitCountBb =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bBassEnabled = LVM_TRUE;
                 break;
             case LVM_EQUALIZER:
                 if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already enabled");
+                    LOGV("\tEffect_setEnabled() LVM_EQUALIZER is already enabled");
                     return -EINVAL;
                 }
+                if(pContext->pBundledContext->SamplesToExitCountEq <= 0){
+                    pContext->pBundledContext->NumberEffectsEnabled++;
+                }
                 pContext->pBundledContext->SamplesToExitCountEq =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bEqualizerEnabled = LVM_TRUE;
                 break;
             case LVM_VIRTUALIZER:
                 if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already enabled");
+                    LOGV("\tEffect_setEnabled() LVM_VIRTUALIZER is already enabled");
                     return -EINVAL;
                 }
+                if(pContext->pBundledContext->SamplesToExitCountVirt <= 0){
+                    pContext->pBundledContext->NumberEffectsEnabled++;
+                }
                 pContext->pBundledContext->SamplesToExitCountVirt =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
                 break;
             case LVM_VOLUME:
                 if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already enabled");
+                    LOGV("\tEffect_setEnabled() LVM_VOLUME is already enabled");
                     return -EINVAL;
                 }
+                pContext->pBundledContext->NumberEffectsEnabled++;
                 pContext->pBundledContext->bVolumeEnabled = LVM_TRUE;
                 break;
             default:
-                LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type");
+                LOGV("\tEffect_setEnabled() invalid effect type");
                 return -EINVAL;
         }
-        pContext->pBundledContext->NumberEffectsEnabled++;
         LvmEffect_enable(pContext);
     } else {
         switch (pContext->EffectType) {
             case LVM_BASS_BOOST:
                 if (pContext->pBundledContext->bBassEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_BASS_BOOST is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bBassEnabled = LVM_FALSE;
                 break;
             case LVM_EQUALIZER:
                 if (pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_EQUALIZER is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE;
                 break;
             case LVM_VIRTUALIZER:
                 if (pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_VIRTUALIZER is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE;
                 break;
             case LVM_VOLUME:
                 if (pContext->pBundledContext->bVolumeEnabled == LVM_FALSE) {
-                    LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already disabled");
+                    LOGV("\tEffect_setEnabled() LVM_VOLUME is already disabled");
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bVolumeEnabled = LVM_FALSE;
                 break;
             default:
-                LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type");
+                LOGV("\tEffect_setEnabled() invalid effect type");
                 return -EINVAL;
         }
-        pContext->pBundledContext->NumberEffectsEnabled--;
         LvmEffect_disable(pContext);
     }
 
     return 0;
 }
 
+//----------------------------------------------------------------------------
+// LVC_Convert_VolToDb()
+//----------------------------------------------------------------------------
+// Purpose:
+// Convery volume in Q24 to dB
+//
+// Inputs:
+//  vol:   Q.24 volume dB
+//
+//-----------------------------------------------------------------------
+
+int16_t LVC_Convert_VolToDb(uint32_t vol){
+    int16_t  dB;
+
+    dB = LVC_ToDB_s32Tos16(vol <<7);
+    dB = (dB +8)>>4;
+    dB = (dB <-96) ? -96 : dB ;
+
+    return dB;
+}
+
 } // namespace
 } // namespace
 
@@ -2493,32 +2550,31 @@
                               audio_buffer_t         *inBuffer,
                               audio_buffer_t         *outBuffer){
     EffectContext * pContext = (EffectContext *) self;
-    LVM_ControlParams_t     ActiveParams;                           /* Current control Parameters */
     LVM_ReturnStatus_en     LvmStatus = LVM_SUCCESS;                /* Function call status */
     int    status = 0;
-    int    status2Sec = 0;
     int    lvmStatus = 0;
     LVM_INT16   *in  = (LVM_INT16 *)inBuffer->raw;
     LVM_INT16   *out = (LVM_INT16 *)outBuffer->raw;
 
-//LOGV("\tEffect_process Start : Enabled = %d     Called = %d",
-//pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled);
-//    LOGV("\tEffect_process Start : Samples left %d %d %d",
+//LOGV("\tEffect_process Start : Enabled = %d     Called = %d (%8d %8d %8d)",
+//pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled,
 //    pContext->pBundledContext->SamplesToExitCountBb,
 //    pContext->pBundledContext->SamplesToExitCountVirt,
 //    pContext->pBundledContext->SamplesToExitCountEq);
 
-//    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-//    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeGetStereoPosition")
-//    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-//    LOGV("\tEffect_process Internal Operating Modes: BB %d   VIRT %d    EQ %d",
-//        ActiveParams.BE_OperatingMode, ActiveParams.VirtualizerOperatingMode,
-//        ActiveParams.EQNB_OperatingMode);
-
     if (pContext == NULL){
         LOGV("\tLVM_ERROR : Effect_process() ERROR pContext == NULL");
         return -EINVAL;
     }
+
+    //if(pContext->EffectType == LVM_BASS_BOOST){
+    //  LOGV("\tEffect_process: Effect type is BASS_BOOST");
+    //}else if(pContext->EffectType == LVM_EQUALIZER){
+    //  LOGV("\tEffect_process: Effect type is LVM_EQUALIZER");
+    //}else if(pContext->EffectType == LVM_VIRTUALIZER){
+    //  LOGV("\tEffect_process: Effect type is LVM_VIRTUALIZER");
+    //}
+
     if (inBuffer == NULL  || inBuffer->raw == NULL  ||
             outBuffer == NULL || outBuffer->raw == NULL ||
             inBuffer->frameCount != outBuffer->frameCount){
@@ -2529,70 +2585,57 @@
         (pContext->EffectType == LVM_BASS_BOOST)){
         //LOGV("\tEffect_process() LVM_BASS_BOOST Effect is not enabled");
         if(pContext->pBundledContext->SamplesToExitCountBb > 0){
-            status2Sec = -ENODATA;
             pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * 2; // STEREO
             //LOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountBb);
         } else {
             status = -ENODATA;
+            pContext->pBundledContext->NumberEffectsEnabled--;
         }
     }
     if ((pContext->pBundledContext->bVolumeEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_VOLUME)){
         //LOGV("\tEffect_process() LVM_VOLUME Effect is not enabled");
         status = -ENODATA;
+        pContext->pBundledContext->NumberEffectsEnabled--;
     }
     if ((pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_EQUALIZER)){
         //LOGV("\tEffect_process() LVM_EQUALIZER Effect is not enabled");
         if(pContext->pBundledContext->SamplesToExitCountEq > 0){
-            status2Sec = -ENODATA;
             pContext->pBundledContext->SamplesToExitCountEq -= outBuffer->frameCount * 2; // STEREO
-            //LOGV("\tEffect_process: Waiting for 2 secs to turn off EQUALIZER, %d samples left",
+            //LOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountEq);
         } else {
             status = -ENODATA;
+            pContext->pBundledContext->NumberEffectsEnabled--;
         }
     }
     if ((pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_VIRTUALIZER)){
         //LOGV("\tEffect_process() LVM_VIRTUALIZER Effect is not enabled");
         if(pContext->pBundledContext->SamplesToExitCountVirt > 0){
-            status2Sec = -ENODATA;
             pContext->pBundledContext->SamplesToExitCountVirt -= outBuffer->frameCount * 2;// STEREO
-            //LOGV("\tEffect_process: Waiting for 2 secs to turn off VIRTUALIZER, %d samples left",
+            //LOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountVirt);
         } else {
             status = -ENODATA;
+            pContext->pBundledContext->NumberEffectsEnabled--;
         }
     }
 
-    // If this is the last frame of an effect process its output with no effect
-    if(status == -ENODATA){
-        if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
-            //LOGV("\tLVM_ERROR : Effect_process() accumulating last frame into output buffer");
-            //LOGV("\tLVM_ERROR : Effect_process() trying copying last frame into output buffer");
-            //LOGV("\tLVM_ERROR : Enabled = %d     Called = %d",
-            //pContext->pBundledContext->NumberEffectsEnabled,
-            //pContext->pBundledContext->NumberEffectsCalled);
-
-        }else{
-            //LOGV("\tLVM_ERROR : Effect_process() copying last frame into output buffer");
-        }
-    }
-
-    if((status2Sec != -ENODATA)&&(status != -ENODATA)){
+    if(status != -ENODATA){
         pContext->pBundledContext->NumberEffectsCalled++;
     }
 
     if(pContext->pBundledContext->NumberEffectsCalled ==
        pContext->pBundledContext->NumberEffectsEnabled){
-        //LOGV("\tEffect_process Calling process with %d effects enabled, %d called: Effect %d",
+        //LOGV("\tEffect_process     Calling process with %d effects enabled, %d called: Effect %d",
         //pContext->pBundledContext->NumberEffectsEnabled,
         //pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType);
 
         if(status == -ENODATA){
-            //LOGV("\tLVM_ERROR : Effect_process() actually processing last frame");
+            LOGV("\tEffect_process() processing last frame");
         }
         pContext->pBundledContext->NumberEffectsCalled = 0;
         /* Process all the available frames, block processing is
@@ -2836,10 +2879,10 @@
         case EFFECT_CMD_SET_PARAM:{
             //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_PARAM start");
             if(pContext->EffectType == LVM_BASS_BOOST){
-                //LOGV("\tBassBoost_command EFFECT_CMD_SET_PARAM param %d, *replySize %d, value %d ",
-                //        *(int32_t *)((char *)pCmdData + sizeof(effect_param_t)),
-                //        *replySize,
-                //        *(int16_t *)((char *)pCmdData + sizeof(effect_param_t) + sizeof(int32_t)));
+                //LOGV("\tBassBoost_command EFFECT_CMD_SET_PARAM param %d, *replySize %d, value %d",
+                //       *(int32_t *)((char *)pCmdData + sizeof(effect_param_t)),
+                //       *replySize,
+                //       *(int16_t *)((char *)pCmdData + sizeof(effect_param_t) + sizeof(int32_t)));
 
                 if (pCmdData   == NULL||
                     cmdSize    != (int)(sizeof(effect_param_t) + sizeof(int32_t) +sizeof(int16_t))||
@@ -3038,30 +3081,71 @@
         }
         case EFFECT_CMD_SET_VOLUME:
         {
-            int32_t vol     = *(int32_t *)pCmdData;
-            int16_t dB;
-            int32_t vol_ret[2] = {1<<24,1<<24}; // Apply no volume
+            uint32_t leftVolume, rightVolume;
+            int16_t  leftdB, rightdB;
+            int16_t  maxdB, pandB;
+            int32_t  vol_ret[2] = {1<<24,1<<24}; // Apply no volume
+            int      status = 0;
+            LVM_ControlParams_t     ActiveParams;           /* Current control Parameters */
+            LVM_ReturnStatus_en     LvmStatus=LVM_SUCCESS;  /* Function call status */
 
             // if pReplyData is NULL, VOL_CTRL is delegated to another effect
             if(pReplyData == LVM_NULL){
                 break;
             }
 
-            if(vol==0x1000000){
-                vol -= 1;
+            if (pCmdData == NULL ||
+                cmdSize != 2 * sizeof(uint32_t)) {
+                LOGV("\tLVM_ERROR : Effect_command cmdCode Case: "
+                        "EFFECT_CMD_SET_VOLUME: ERROR");
+                return -EINVAL;
             }
-            // Convert volume linear (Q8.24) to volume dB (0->-96)
-            dB = android::LVC_ToDB_s32Tos16(vol <<7);
-            dB = (dB +8)>>4;
-            dB = (dB <-96) ? -96 : dB ;
 
-            LOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB (%d), "
-                  "effect is %d",
-            pContext->pBundledContext->SessionNo, pContext->pBundledContext->SessionId,
-            (int32_t)dB, vol<<7, pContext->EffectType);
+            leftVolume  = ((*(uint32_t *)pCmdData));
+            rightVolume = ((*((uint32_t *)pCmdData + 1)));
+
+            if(leftVolume == 0x1000000){
+                leftVolume -= 1;
+            }
+            if(rightVolume == 0x1000000){
+                rightVolume -= 1;
+            }
+
+            // Convert volume to dB
+            leftdB  = android::LVC_Convert_VolToDb(leftVolume);
+            rightdB = android::LVC_Convert_VolToDb(rightVolume);
+
+            pandB = rightdB - leftdB;
+
+            // Calculate max volume in dB
+            maxdB = leftdB;
+            if(rightdB > maxdB){
+                maxdB = rightdB;
+            }
+            //LOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB (%d), "
+            //      "effect is %d",
+            //pContext->pBundledContext->SessionNo, pContext->pBundledContext->SessionId,
+            //(int32_t)maxdB, maxVol<<7, pContext->EffectType);
+            //LOGV("\tEFFECT_CMD_SET_VOLUME: Left is %d, Right is %d", leftVolume, rightVolume);
+            //LOGV("\tEFFECT_CMD_SET_VOLUME: Left %ddB, Right %ddB, Position %ddB",
+            //        leftdB, rightdB, pandB);
 
             memcpy(pReplyData, vol_ret, sizeof(int32_t)*2);
-            android::VolumeSetVolumeLevel(pContext, (int16_t)(dB*100));
+            android::VolumeSetVolumeLevel(pContext, (int16_t)(maxdB*100));
+
+            /* Get the current settings */
+            LvmStatus =LVM_GetControlParameters(pContext->pBundledContext->hInstance,&ActiveParams);
+            LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeSetStereoPosition")
+            if(LvmStatus != LVM_SUCCESS) return -EINVAL;
+
+            /* Volume parameters */
+            ActiveParams.VC_Balance  = pandB;
+            LOGV("\t\tVolumeSetStereoPosition() (-96dB -> +96dB)-> %d\n", ActiveParams.VC_Balance );
+
+            /* Activate the initial settings */
+            LvmStatus =LVM_SetControlParameters(pContext->pBundledContext->hInstance,&ActiveParams);
+            LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "VolumeSetStereoPosition")
+            if(LvmStatus != LVM_SUCCESS) return -EINVAL;
             break;
          }
         case EFFECT_CMD_SET_AUDIO_MODE:
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 91963af..2b51029 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -88,12 +88,13 @@
     int                             positionSaved;
     bool                            bMuteEnabled;   /* Must store as mute = -96dB level */
     bool                            bStereoPositionEnabled;
-    int                             frameCount;
     LVM_Fs_en                       SampleRate;
     int                             SamplesPerSecond;
     int                             SamplesToExitCountEq;
     int                             SamplesToExitCountBb;
     int                             SamplesToExitCountVirt;
+    LVM_INT16                       *workBuffer;
+    int                             frameCount;
     #ifdef LVM_PCM
     FILE                            *PcmInPtr;
     FILE                            *PcmOutPtr;
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 03a6bbb..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);
 }
 
 //----------------------------------------------------------------------------
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 ffe1983..877e787 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -534,8 +534,9 @@
         if (f) {
             while (!feof(f)) {
                 fgets(buffer, SIZE, f);
-                if (strstr(buffer, " /sdcard/") ||
+                if (strstr(buffer, " /mnt/sdcard/") ||
                     strstr(buffer, " /system/sounds/") ||
+                    strstr(buffer, " /data/") ||
                     strstr(buffer, " /system/media/")) {
                     result.append("  ");
                     result.append(buffer);
@@ -569,8 +570,9 @@
                                 } else {
                                     linkto[len] = 0;
                                 }
-                                if (strstr(linkto, "/sdcard/") == linkto ||
+                                if (strstr(linkto, "/mnt/sdcard/") == linkto ||
                                     strstr(linkto, "/system/sounds/") == linkto ||
+                                    strstr(linkto, "/data/") == linkto ||
                                     strstr(linkto, "/system/media/") == linkto) {
                                     result.append("  ");
                                     result.append(buffer);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 3bf8ae7..472bfcf 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -18,6 +18,7 @@
         HTTPStream.cpp                    \
         JPEGSource.cpp                    \
         MP3Extractor.cpp                  \
+        MPEG2TSWriter.cpp                 \
         MPEG4Extractor.cpp                \
         MPEG4Writer.cpp                   \
         MediaBuffer.cpp                   \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index c27cfc8..b314114 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -27,9 +27,13 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 
+#include "include/AwesomePlayer.h"
+
 namespace android {
 
-AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
+AudioPlayer::AudioPlayer(
+        const sp<MediaPlayerBase::AudioSink> &audioSink,
+        AwesomePlayer *observer)
     : mAudioTrack(NULL),
       mInputBuffer(NULL),
       mSampleRate(0),
@@ -45,7 +49,8 @@
       mIsFirstBuffer(false),
       mFirstBufferResult(OK),
       mFirstBuffer(NULL),
-      mAudioSink(audioSink) {
+      mAudioSink(audioSink),
+      mObserver(observer) {
 }
 
 AudioPlayer::~AudioPlayer() {
@@ -301,6 +306,9 @@
                 }
 
                 mSeeking = false;
+                if (mObserver) {
+                    mObserver->postAudioSeekComplete();
+                }
             }
         }
 
@@ -323,6 +331,10 @@
             Mutex::Autolock autoLock(mLock);
 
             if (err != OK) {
+                if (mObserver && !mReachedEOS) {
+                    mObserver->postAudioEOS();
+                }
+
                 mReachedEOS = true;
                 mFinalStatus = err;
                 break;
@@ -411,6 +423,12 @@
     mReachedEOS = false;
     mSeekTimeUs = time_us;
 
+    if (mAudioSink != NULL) {
+        mAudioSink->flush();
+    } else {
+        mAudioTrack->flush();
+    }
+
     return OK;
 }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index fd5f30b..97c9003 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -642,7 +642,7 @@
     if (mAudioSource != NULL) {
         if (mAudioPlayer == NULL) {
             if (mAudioSink != NULL) {
-                mAudioPlayer = new AudioPlayer(mAudioSink);
+                mAudioPlayer = new AudioPlayer(mAudioSink, this);
                 mAudioPlayer->setSource(mAudioSource);
 
                 // We've already started the MediaSource in order to enable
@@ -669,8 +669,6 @@
         } else {
             mAudioPlayer->resume();
         }
-
-        postCheckAudioStatusEvent_l();
     }
 
     if (mTimeSource == NULL && mAudioPlayer == NULL) {
@@ -1191,7 +1189,7 @@
         return;
     }
     mAudioStatusEventPending = true;
-    mQueue.postEventWithDelay(mCheckAudioStatusEvent, 100000ll);
+    mQueue.postEvent(mCheckAudioStatusEvent);
 }
 
 void AwesomePlayer::onCheckAudioStatus() {
@@ -1222,8 +1220,6 @@
         mFlags |= FIRST_FRAME;
         postStreamDoneEvent_l(finalStatus);
     }
-
-    postCheckAudioStatusEvent_l();
 }
 
 status_t AwesomePlayer::prepare() {
@@ -1685,5 +1681,13 @@
     return mExtractorFlags;
 }
 
+void AwesomePlayer::postAudioEOS() {
+    postCheckAudioStatusEvent_l();
+}
+
+void AwesomePlayer::postAudioSeekComplete() {
+    postCheckAudioStatusEvent_l();
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
new file mode 100644
index 0000000..ee74b88
--- /dev/null
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MPEG2TSWriter"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MPEG2TSWriter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/ESDS.h"
+
+namespace android {
+
+struct MPEG2TSWriter::SourceInfo : public AHandler {
+    SourceInfo(const sp<MediaSource> &source);
+
+    void start(const sp<AMessage> &notify);
+    void stop();
+
+    unsigned streamType() const;
+    unsigned incrementContinuityCounter();
+
+    enum {
+        kNotifyStartFailed,
+        kNotifyBuffer,
+        kNotifyReachedEOS,
+    };
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+    virtual ~SourceInfo();
+
+private:
+    enum {
+        kWhatStart = 'strt',
+        kWhatRead  = 'read',
+    };
+
+    sp<MediaSource> mSource;
+    sp<ALooper> mLooper;
+    sp<AMessage> mNotify;
+
+    sp<ABuffer> mAACBuffer;
+
+    unsigned mStreamType;
+    unsigned mContinuityCounter;
+
+    void extractCodecSpecificData();
+
+    void appendAACFrames(MediaBuffer *buffer);
+    void flushAACFrames();
+
+    void postAVCFrame(MediaBuffer *buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SourceInfo);
+};
+
+MPEG2TSWriter::SourceInfo::SourceInfo(const sp<MediaSource> &source)
+    : mSource(source),
+      mLooper(new ALooper),
+      mStreamType(0),
+      mContinuityCounter(0) {
+    mLooper->setName("MPEG2TSWriter source");
+
+    sp<MetaData> meta = mSource->getFormat();
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        mStreamType = 0x0f;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        mStreamType = 0x1b;
+    } else {
+        TRESPASS();
+    }
+}
+
+MPEG2TSWriter::SourceInfo::~SourceInfo() {
+}
+
+unsigned MPEG2TSWriter::SourceInfo::streamType() const {
+    return mStreamType;
+}
+
+unsigned MPEG2TSWriter::SourceInfo::incrementContinuityCounter() {
+    if (++mContinuityCounter == 16) {
+        mContinuityCounter = 0;
+    }
+
+    return mContinuityCounter;
+}
+
+void MPEG2TSWriter::SourceInfo::start(const sp<AMessage> &notify) {
+    mLooper->registerHandler(this);
+    mLooper->start();
+
+    mNotify = notify;
+
+    (new AMessage(kWhatStart, id()))->post();
+}
+
+void MPEG2TSWriter::SourceInfo::stop() {
+    mLooper->unregisterHandler(id());
+    mLooper->stop();
+}
+
+void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
+    sp<MetaData> meta = mSource->getFormat();
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        return;
+    }
+
+    sp<ABuffer> out = new ABuffer(1024);
+    out->setRange(0, 0);
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    CHECK(meta->findData(kKeyAVCC, &type, &data, &size));
+
+    const uint8_t *ptr = (const uint8_t *)data;
+
+    size_t numSeqParameterSets = ptr[5] & 31;
+
+    ptr += 6;
+    size -= 6;
+
+    for (size_t i = 0; i < numSeqParameterSets; ++i) {
+        CHECK(size >= 2);
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        CHECK(size >= length);
+
+        CHECK_LE(out->size() + 4 + length, out->capacity());
+        memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4);
+        memcpy(out->data() + out->size() + 4, ptr, length);
+        out->setRange(0, out->size() + length + 4);
+
+        ptr += length;
+        size -= length;
+    }
+
+    CHECK(size >= 1);
+    size_t numPictureParameterSets = *ptr;
+    ++ptr;
+    --size;
+
+    for (size_t i = 0; i < numPictureParameterSets; ++i) {
+        CHECK(size >= 2);
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        CHECK(size >= length);
+
+        CHECK_LE(out->size() + 4 + length, out->capacity());
+        memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4);
+        memcpy(out->data() + out->size() + 4, ptr, length);
+        out->setRange(0, out->size() + length + 4);
+
+        ptr += length;
+        size -= length;
+    }
+
+    out->meta()->setInt64("timeUs", 0ll);
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kNotifyBuffer);
+    notify->setObject("buffer", out);
+    notify->post();
+}
+
+void MPEG2TSWriter::SourceInfo::postAVCFrame(MediaBuffer *buffer) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kNotifyBuffer);
+
+    sp<ABuffer> copy =
+        new ABuffer(buffer->range_length());
+    memcpy(copy->data(),
+           (const uint8_t *)buffer->data()
+            + buffer->range_offset(),
+           buffer->range_length());
+
+    int64_t timeUs;
+    CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+    copy->meta()->setInt64("timeUs", timeUs);
+
+    int32_t isSync;
+    if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)
+            && isSync != 0) {
+        copy->meta()->setInt32("isSync", true);
+    }
+
+    notify->setObject("buffer", copy);
+    notify->post();
+}
+
+void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) {
+    if (mAACBuffer != NULL
+            && mAACBuffer->size() + 7 + buffer->range_length()
+                    > mAACBuffer->capacity()) {
+        flushAACFrames();
+    }
+
+    if (mAACBuffer == NULL) {
+        size_t alloc = 4096;
+        if (buffer->range_length() + 7 > alloc) {
+            alloc = 7 + buffer->range_length();
+        }
+
+        mAACBuffer = new ABuffer(alloc);
+
+        int64_t timeUs;
+        CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+        mAACBuffer->meta()->setInt64("timeUs", timeUs);
+        mAACBuffer->meta()->setInt32("isSync", true);
+
+        mAACBuffer->setRange(0, 0);
+    }
+
+    sp<MetaData> meta = mSource->getFormat();
+    uint32_t type;
+    const void *data;
+    size_t size;
+    CHECK(meta->findData(kKeyESDS, &type, &data, &size));
+
+    ESDS esds((const char *)data, size);
+    CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+    const uint8_t *codec_specific_data;
+    size_t codec_specific_data_size;
+    esds.getCodecSpecificInfo(
+            (const void **)&codec_specific_data, &codec_specific_data_size);
+
+    CHECK_GE(codec_specific_data_size, 2u);
+
+    unsigned profile = (codec_specific_data[0] >> 3) - 1;
+
+    unsigned sampling_freq_index =
+        ((codec_specific_data[0] & 7) << 1)
+        | (codec_specific_data[1] >> 7);
+
+    unsigned channel_configuration =
+        (codec_specific_data[1] >> 3) & 0x0f;
+
+    uint8_t *ptr = mAACBuffer->data() + mAACBuffer->size();
+
+    const uint32_t aac_frame_length = buffer->range_length() + 7;
+
+    *ptr++ = 0xff;
+    *ptr++ = 0xf1;  // b11110001, ID=0, layer=0, protection_absent=1
+
+    *ptr++ =
+        profile << 6
+        | sampling_freq_index << 2
+        | ((channel_configuration >> 2) & 1);  // private_bit=0
+
+    // original_copy=0, home=0, copyright_id_bit=0, copyright_id_start=0
+    *ptr++ =
+        (channel_configuration & 3) << 6
+        | aac_frame_length >> 11;
+    *ptr++ = (aac_frame_length >> 3) & 0xff;
+    *ptr++ = (aac_frame_length & 7) << 5;
+
+    // adts_buffer_fullness=0, number_of_raw_data_blocks_in_frame=0
+    *ptr++ = 0;
+
+    memcpy(ptr,
+           (const uint8_t *)buffer->data() + buffer->range_offset(),
+           buffer->range_length());
+
+    ptr += buffer->range_length();
+
+    mAACBuffer->setRange(0, ptr - mAACBuffer->data());
+}
+
+void MPEG2TSWriter::SourceInfo::flushAACFrames() {
+    if (mAACBuffer == NULL) {
+        return;
+    }
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kNotifyBuffer);
+    notify->setObject("buffer", mAACBuffer);
+    notify->post();
+
+    mAACBuffer.clear();
+}
+
+void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatStart:
+        {
+            status_t err = mSource->start();
+            if (err != OK) {
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("what", kNotifyStartFailed);
+                notify->post();
+                break;
+            }
+
+            extractCodecSpecificData();
+
+            (new AMessage(kWhatRead, id()))->post();
+            break;
+        }
+
+        case kWhatRead:
+        {
+            MediaBuffer *buffer;
+            status_t err = mSource->read(&buffer);
+
+            if (err != OK && err != INFO_FORMAT_CHANGED) {
+                if (mStreamType == 0x0f) {
+                    flushAACFrames();
+                }
+
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("what", kNotifyReachedEOS);
+                notify->setInt32("status", err);
+                notify->post();
+                break;
+            }
+
+            if (err == OK) {
+                if (buffer->range_length() > 0) {
+                    if (mStreamType == 0x0f) {
+                        appendAACFrames(buffer);
+                    } else {
+                        postAVCFrame(buffer);
+                    }
+                }
+
+                buffer->release();
+                buffer = NULL;
+            }
+
+            msg->post();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG2TSWriter::MPEG2TSWriter(const char *filename)
+    : mFile(fopen(filename, "wb")),
+      mStarted(false),
+      mNumSourcesDone(0),
+      mNumTSPacketsWritten(0),
+      mNumTSPacketsBeforeMeta(0) {
+    CHECK(mFile != NULL);
+
+    mLooper = new ALooper;
+    mLooper->setName("MPEG2TSWriter");
+
+    mReflector = new AHandlerReflector<MPEG2TSWriter>(this);
+
+    mLooper->registerHandler(mReflector);
+    mLooper->start();
+}
+
+MPEG2TSWriter::~MPEG2TSWriter() {
+    mLooper->unregisterHandler(mReflector->id());
+    mLooper->stop();
+
+    fclose(mFile);
+    mFile = NULL;
+}
+
+status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) {
+    CHECK(!mStarted);
+
+    sp<MetaData> meta = source->getFormat();
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
+            && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    sp<SourceInfo> info = new SourceInfo(source);
+
+    mSources.push(info);
+
+    return OK;
+}
+
+status_t MPEG2TSWriter::start(MetaData *param) {
+    CHECK(!mStarted);
+
+    mStarted = true;
+    mNumSourcesDone = 0;
+    mNumTSPacketsWritten = 0;
+    mNumTSPacketsBeforeMeta = 0;
+
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        sp<AMessage> notify =
+            new AMessage(kWhatSourceNotify, mReflector->id());
+
+        notify->setInt32("source-index", i);
+
+        mSources.editItemAt(i)->start(notify);
+    }
+
+    return OK;
+}
+
+status_t MPEG2TSWriter::stop() {
+    CHECK(mStarted);
+
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        mSources.editItemAt(i)->stop();
+    }
+    mStarted = false;
+
+    return OK;
+}
+
+status_t MPEG2TSWriter::pause() {
+    CHECK(mStarted);
+
+    return OK;
+}
+
+bool MPEG2TSWriter::reachedEOS() {
+    return !mStarted || (mNumSourcesDone == mSources.size() ? true : false);
+}
+
+status_t MPEG2TSWriter::dump(int fd, const Vector<String16> &args) {
+    return OK;
+}
+
+void MPEG2TSWriter::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSourceNotify:
+        {
+            int32_t sourceIndex;
+            CHECK(msg->findInt32("source-index", &sourceIndex));
+
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            if (what == SourceInfo::kNotifyReachedEOS
+                    || what == SourceInfo::kNotifyStartFailed) {
+                ++mNumSourcesDone;
+            } else if (what == SourceInfo::kNotifyBuffer) {
+                sp<RefBase> obj;
+                CHECK(msg->findObject("buffer", &obj));
+
+                writeTS();
+
+                sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+                writeAccessUnit(sourceIndex, buffer);
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MPEG2TSWriter::writeProgramAssociationTable() {
+    // 0x47
+    // transport_error_indicator = b0
+    // payload_unit_start_indicator = b1
+    // transport_priority = b0
+    // PID = b0000000000000 (13 bits)
+    // transport_scrambling_control = b00
+    // adaptation_field_control = b01 (no adaptation field, payload only)
+    // continuity_counter = b????
+    // skip = 0x00
+    // --- payload follows
+    // table_id = 0x00
+    // section_syntax_indicator = b1
+    // must_be_zero = b0
+    // reserved = b11
+    // section_length = 0x00d
+    // transport_stream_id = 0x0000
+    // reserved = b11
+    // version_number = b00001
+    // current_next_indicator = b1
+    // section_number = 0x00
+    // last_section_number = 0x00
+    //   one program follows:
+    //   program_number = 0x0001
+    //   reserved = b111
+    //   program_map_PID = 0x01e0 (13 bits!)
+    // CRC = 0x????????
+
+    static const uint8_t kData[] = {
+        0x47,
+        0x40, 0x00, 0x10, 0x00,  // b0100 0000 0000 0000 0001 ???? 0000 0000
+        0x00, 0xb0, 0x0d, 0x00,  // b0000 0000 1011 0000 0000 1101 0000 0000
+        0x00, 0xc3, 0x00, 0x00,  // b0000 0000 1100 0011 0000 0000 0000 0000
+        0x00, 0x01, 0xe1, 0xe0,  // b0000 0000 0000 0001 1110 0001 1110 0000
+        0x00, 0x00, 0x00, 0x00   // b???? ???? ???? ???? ???? ???? ???? ????
+    };
+
+    sp<ABuffer> buffer = new ABuffer(188);
+    memset(buffer->data(), 0, buffer->size());
+    memcpy(buffer->data(), kData, sizeof(kData));
+
+    static const unsigned kContinuityCounter = 5;
+    buffer->data()[3] |= kContinuityCounter;
+
+    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+}
+
+void MPEG2TSWriter::writeProgramMap() {
+    // 0x47
+    // transport_error_indicator = b0
+    // payload_unit_start_indicator = b1
+    // transport_priority = b0
+    // PID = b0 0001 1110 0000 (13 bits) [0x1e0]
+    // transport_scrambling_control = b00
+    // adaptation_field_control = b01 (no adaptation field, payload only)
+    // continuity_counter = b????
+    // skip = 0x00
+    // -- payload follows
+    // table_id = 0x02
+    // section_syntax_indicator = b1
+    // must_be_zero = b0
+    // reserved = b11
+    // section_length = 0x???
+    // program_number = 0x0001
+    // reserved = b11
+    // version_number = b00001
+    // current_next_indicator = b1
+    // section_number = 0x00
+    // last_section_number = 0x00
+    // reserved = b111
+    // PCR_PID = b? ???? ???? ???? (13 bits)
+    // reserved = b1111
+    // program_info_length = 0x000
+    //   one or more elementary stream descriptions follow:
+    //   stream_type = 0x??
+    //   reserved = b111
+    //   elementary_PID = b? ???? ???? ???? (13 bits)
+    //   reserved = b1111
+    //   ES_info_length = 0x000
+    // CRC = 0x????????
+
+    static const uint8_t kData[] = {
+        0x47,
+        0x41, 0xe0, 0x10, 0x00,  // b0100 0001 1110 0000 0001 ???? 0000 0000
+        0x02, 0xb0, 0x00, 0x00,  // b0000 0010 1011 ???? ???? ???? 0000 0000
+        0x01, 0xc3, 0x00, 0x00,  // b0000 0001 1100 0011 0000 0000 0000 0000
+        0xe0, 0x00, 0xf0, 0x00   // b111? ???? ???? ???? 1111 0000 0000 0000
+    };
+
+    sp<ABuffer> buffer = new ABuffer(188);
+    memset(buffer->data(), 0, buffer->size());
+    memcpy(buffer->data(), kData, sizeof(kData));
+
+    static const unsigned kContinuityCounter = 5;
+    buffer->data()[3] |= kContinuityCounter;
+
+    size_t section_length = 5 * mSources.size() + 4 + 9;
+    buffer->data()[6] |= section_length >> 8;
+    buffer->data()[7] = section_length & 0xff;
+
+    static const unsigned kPCR_PID = 0x1e1;
+    buffer->data()[13] |= (kPCR_PID >> 8) & 0x1f;
+    buffer->data()[14] = kPCR_PID & 0xff;
+
+    uint8_t *ptr = &buffer->data()[sizeof(kData)];
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        *ptr++ = mSources.editItemAt(i)->streamType();
+
+        const unsigned ES_PID = 0x1e0 + i + 1;
+        *ptr++ = 0xe0 | (ES_PID >> 8);
+        *ptr++ = ES_PID & 0xff;
+        *ptr++ = 0xf0;
+        *ptr++ = 0x00;
+    }
+
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+
+    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+}
+
+void MPEG2TSWriter::writeAccessUnit(
+        int32_t sourceIndex, const sp<ABuffer> &accessUnit) {
+    // 0x47
+    // transport_error_indicator = b0
+    // payload_unit_start_indicator = b1
+    // transport_priority = b0
+    // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex]
+    // transport_scrambling_control = b00
+    // adaptation_field_control = b01 (no adaptation field, payload only)
+    // continuity_counter = b????
+    // -- payload follows
+    // packet_startcode_prefix = 0x000001
+    // stream_id = 0x?? (0xe0 for avc video, 0xc0 for aac audio)
+    // PES_packet_length = 0x????
+    // reserved = b10
+    // PES_scrambling_control = b00
+    // PES_priority = b0
+    // data_alignment_indicator = b1
+    // copyright = b0
+    // original_or_copy = b0
+    // PTS_DTS_flags = b10  (PTS only)
+    // ESCR_flag = b0
+    // ES_rate_flag = b0
+    // DSM_trick_mode_flag = b0
+    // additional_copy_info_flag = b0
+    // PES_CRC_flag = b0
+    // PES_extension_flag = b0
+    // PES_header_data_length = 0x05
+    // reserved = b0010 (PTS)
+    // PTS[32..30] = b???
+    // reserved = b1
+    // PTS[29..15] = b??? ???? ???? ???? (15 bits)
+    // reserved = b1
+    // PTS[14..0] = b??? ???? ???? ???? (15 bits)
+    // reserved = b1
+    // the first fragment of "buffer" follows
+
+    sp<ABuffer> buffer = new ABuffer(188);
+    memset(buffer->data(), 0, buffer->size());
+
+    const unsigned PID = 0x1e0 + sourceIndex + 1;
+
+    const unsigned continuity_counter =
+        mSources.editItemAt(sourceIndex)->incrementContinuityCounter();
+
+    // XXX if there are multiple streams of a kind (more than 1 audio or
+    // more than 1 video) they need distinct stream_ids.
+    const unsigned stream_id =
+        mSources.editItemAt(sourceIndex)->streamType() == 0x0f ? 0xc0 : 0xe0;
+
+    int64_t timeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+    uint32_t PTS = (timeUs * 9ll) / 100ll;
+
+    size_t PES_packet_length = accessUnit->size() + 8;
+
+    uint8_t *ptr = buffer->data();
+    *ptr++ = 0x47;
+    *ptr++ = 0x40 | (PID >> 8);
+    *ptr++ = PID & 0xff;
+    *ptr++ = 0x10 | continuity_counter;
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+    *ptr++ = 0x01;
+    *ptr++ = stream_id;
+    *ptr++ = PES_packet_length >> 8;
+    *ptr++ = PES_packet_length & 0xff;
+    *ptr++ = 0x84;
+    *ptr++ = 0x80;
+    *ptr++ = 0x05;
+    *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1;
+    *ptr++ = (PTS >> 22) & 0xff;
+    *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1;
+    *ptr++ = (PTS >> 7) & 0xff;
+    *ptr++ = ((PTS & 0x7f) << 1) | 1;
+
+    size_t sizeLeft = buffer->data() + buffer->size() - ptr;
+    size_t copy = accessUnit->size();
+    if (copy > sizeLeft) {
+        copy = sizeLeft;
+    }
+
+    memcpy(ptr, accessUnit->data(), copy);
+
+    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+
+    size_t offset = copy;
+    while (offset < accessUnit->size()) {
+        // for subsequent fragments of "buffer":
+        // 0x47
+        // transport_error_indicator = b0
+        // payload_unit_start_indicator = b0
+        // transport_priority = b0
+        // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex]
+        // transport_scrambling_control = b00
+        // adaptation_field_control = b01 (no adaptation field, payload only)
+        // continuity_counter = b????
+        // the fragment of "buffer" follows.
+
+        memset(buffer->data(), 0, buffer->size());
+
+        const unsigned continuity_counter =
+            mSources.editItemAt(sourceIndex)->incrementContinuityCounter();
+
+        ptr = buffer->data();
+        *ptr++ = 0x47;
+        *ptr++ = 0x00 | (PID >> 8);
+        *ptr++ = PID & 0xff;
+        *ptr++ = 0x10 | continuity_counter;
+
+        size_t sizeLeft = buffer->data() + buffer->size() - ptr;
+        size_t copy = accessUnit->size() - offset;
+        if (copy > sizeLeft) {
+            copy = sizeLeft;
+        }
+
+        memcpy(ptr, accessUnit->data() + offset, copy);
+        CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile),
+                 buffer->size());
+
+        offset += copy;
+    }
+}
+
+void MPEG2TSWriter::writeTS() {
+    if (mNumTSPacketsWritten >= mNumTSPacketsBeforeMeta) {
+        writeProgramAssociationTable();
+        writeProgramMap();
+
+        mNumTSPacketsBeforeMeta = mNumTSPacketsWritten + 2500;
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index ba1e218..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
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 332bab3..2743b2f 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/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 7a8cf32..43938b2 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -93,7 +93,10 @@
     sp<DataSource> mSource;
     off_t mOffset;
     Page mCurrentPage;
+    uint64_t mPrevGranulePosition;
     size_t mCurrentPageSize;
+    bool mFirstPacketInPage;
+    uint64_t mCurrentPageSamples;
     size_t mNextLaceIndex;
 
     off_t mFirstDataOffset;
@@ -113,6 +116,8 @@
     void parseFileMetaData();
     void extractAlbumArt(const void *data, size_t size);
 
+    uint64_t findPrevGranulePosition(off_t pageOffset);
+
     MyVorbisExtractor(const MyVorbisExtractor &);
     MyVorbisExtractor &operator=(const MyVorbisExtractor &);
 };
@@ -193,7 +198,10 @@
 MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source)
     : mSource(source),
       mOffset(0),
+      mPrevGranulePosition(0),
       mCurrentPageSize(0),
+      mFirstPacketInPage(true),
+      mCurrentPageSamples(0),
       mNextLaceIndex(0),
       mFirstDataOffset(-1) {
     mCurrentPage.mNumSegments = 0;
@@ -238,6 +246,52 @@
     }
 }
 
+// Given the offset of the "current" page, find the page immediately preceding
+// it (if any) and return its granule position.
+// To do this we back up from the "current" page's offset until we find any
+// page preceding it and then scan forward to just before the current page.
+uint64_t MyVorbisExtractor::findPrevGranulePosition(off_t pageOffset) {
+    off_t prevPageOffset = 0;
+    off_t prevGuess = pageOffset;
+    for (;;) {
+        if (prevGuess >= 5000) {
+            prevGuess -= 5000;
+        } else {
+            prevGuess = 0;
+        }
+
+        LOGV("backing up %ld bytes", pageOffset - prevGuess);
+
+        CHECK_EQ(findNextPage(prevGuess, &prevPageOffset), (status_t)OK);
+
+        if (prevPageOffset < pageOffset || prevGuess == 0) {
+            break;
+        }
+    }
+
+    if (prevPageOffset == pageOffset) {
+        // We did not find a page preceding this one.
+        return 0;
+    }
+
+    LOGV("prevPageOffset at %ld, pageOffset at %ld", prevPageOffset, pageOffset);
+
+    for (;;) {
+        Page prevPage;
+        ssize_t n = readPage(prevPageOffset, &prevPage);
+
+        if (n <= 0) {
+            return 0;
+        }
+
+        prevPageOffset += n;
+
+        if (prevPageOffset == pageOffset) {
+            return prevPage.mGranulePosition;
+        }
+    }
+}
+
 status_t MyVorbisExtractor::seekToOffset(off_t offset) {
     if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
         // Once we know where the actual audio data starts (past the headers)
@@ -252,9 +306,16 @@
         return err;
     }
 
+    // We found the page we wanted to seek to, but we'll also need
+    // the page preceding it to determine how many valid samples are on
+    // this page.
+    mPrevGranulePosition = findPrevGranulePosition(pageOffset);
+
     mOffset = pageOffset;
 
     mCurrentPageSize = 0;
+    mFirstPacketInPage = true;
+    mCurrentPageSamples = 0;
     mCurrentPage.mNumSegments = 0;
     mNextLaceIndex = 0;
 
@@ -399,6 +460,12 @@
                     buffer->meta_data()->setInt64(kKeyTime, timeUs);
                 }
 
+                if (mFirstPacketInPage) {
+                    buffer->meta_data()->setInt32(
+                            kKeyValidSamples, mCurrentPageSamples);
+                    mFirstPacketInPage = false;
+                }
+
                 *out = buffer;
 
                 return OK;
@@ -423,6 +490,12 @@
             return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
         }
 
+        mCurrentPageSamples =
+            mCurrentPage.mGranulePosition - mPrevGranulePosition;
+        mFirstPacketInPage = true;
+
+        mPrevGranulePosition = mCurrentPage.mGranulePosition;
+
         mCurrentPageSize = n;
         mNextLaceIndex = 0;
 
@@ -435,6 +508,10 @@
                     buffer->meta_data()->setInt64(kKeyTime, timeUs);
                 }
 
+                buffer->meta_data()->setInt32(
+                        kKeyValidSamples, mCurrentPageSamples);
+                mFirstPacketInPage = false;
+
                 *out = buffer;
 
                 return OK;
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index e4ed5e6..f58c16d 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -171,6 +171,10 @@
             mInputBuffer->release();
             mInputBuffer = NULL;
         }
+
+        // Make sure that the next buffer output does not still
+        // depend on fragments from the last one decoded.
+        PVMP4AudioDecoderResetBuffer(mDecoderBuf);
     } else {
         seekTimeUs = -1;
     }
diff --git a/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h b/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h
index 0620367..3ebd929 100644
--- a/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h
+++ b/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h
@@ -87,22 +87,22 @@
 
     enum Frame_Type_3GPP
     {
-        AMR_475 = 0,
-        AMR_515,
-        AMR_59,
-        AMR_67,
-        AMR_74,
-        AMR_795,
-        AMR_102,
-        AMR_122,
-        AMR_SID,
-        GSM_EFR_SID,
-        TDMA_EFR_SID,
-        PDC_EFR_SID,
-        FOR_FUTURE_USE1,
-        FOR_FUTURE_USE2,
-        FOR_FUTURE_USE3,
-        AMR_NO_DATA
+        AMR_475 = 0,        /* 4.75 kbps    */
+        AMR_515,            /* 5.15 kbps    */
+        AMR_59,             /* 5.9 kbps     */
+        AMR_67,             /* 6.7 kbps     */
+        AMR_74,             /* 7.4 kbps     */
+        AMR_795,            /* 7.95 kbps    */
+        AMR_102,            /* 10.2 kbps    */
+        AMR_122,            /* 12.2 kbps    */
+        AMR_SID,            /* GSM AMR DTX  */
+        GSM_EFR_SID,        /* GSM EFR DTX  */
+        TDMA_EFR_SID,       /* TDMA EFR DTX */
+        PDC_EFR_SID,        /* PDC EFR DTX  */
+        FOR_FUTURE_USE1,    /* Unused 1     */
+        FOR_FUTURE_USE2,    /* Unused 2     */
+        FOR_FUTURE_USE3,    /* Unused 3     */
+        AMR_NO_DATA         /* No data      */
     };
 
     /*----------------------------------------------------------------------------
diff --git a/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h b/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h
index 673a94a..a9fdb1c 100644
--- a/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h
+++ b/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h
@@ -87,6 +87,8 @@
 
 #include "gsm_amr_typedefs.h"
 #include "pvamrnbdecoder_api.h"
+#include "frame_type_3gpp.h"
+
 /*--------------------------------------------------------------------------*/
 #ifdef __cplusplus
 extern "C"
@@ -117,25 +119,6 @@
     /*----------------------------------------------------------------------------
     ; ENUMERATED TYPEDEF'S
     ----------------------------------------------------------------------------*/
-    enum Frame_Type_3GPP
-    {
-        AMR_475 = 0,        /* 4.75 kbps    */
-        AMR_515,            /* 5.15 kbps    */
-        AMR_59,             /* 5.9 kbps     */
-        AMR_67,             /* 6.7 kbps     */
-        AMR_74,             /* 7.4 kbps     */
-        AMR_795,            /* 7.95 kbps    */
-        AMR_102,            /* 10.2 kbps    */
-        AMR_122,            /* 12.2 kbps    */
-        AMR_SID,            /* GSM AMR DTX  */
-        GSM_EFR_SID,        /* GSM EFR DTX  */
-        TDMA_EFR_SID,       /* TDMA EFR DTX */
-        PDC_EFR_SID,        /* PDC EFR DTX  */
-        FOR_FUTURE_USE1,    /* Unused 1     */
-        FOR_FUTURE_USE2,    /* Unused 2     */
-        FOR_FUTURE_USE3,    /* Unused 3     */
-        AMR_NO_DATA
-    };      /* No data      */
 
     /*----------------------------------------------------------------------------
     ; STRUCTURES TYPEDEF'S
diff --git a/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h b/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h
index 390a44d..67c7aa3 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h
+++ b/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h
@@ -86,6 +86,7 @@
 ----------------------------------------------------------------------------*/
 
 #include "gsm_amr_typedefs.h"
+#include "frame_type_3gpp.h"
 
 /*--------------------------------------------------------------------------*/
 #ifdef __cplusplus
@@ -137,26 +138,6 @@
         N_MODES   /* Not Used  */
     };
 
-    enum Frame_Type_3GPP
-    {
-        AMR_475 = 0,        /* 4.75 kbps    */
-        AMR_515,            /* 5.15 kbps    */
-        AMR_59,             /* 5.9 kbps     */
-        AMR_67,             /* 6.7 kbps     */
-        AMR_74,             /* 7.4 kbps     */
-        AMR_795,            /* 7.95 kbps    */
-        AMR_102,            /* 10.2 kbps    */
-        AMR_122,            /* 12.2 kbps    */
-        AMR_SID,            /* GSM AMR DTX  */
-        GSM_EFR_SID,        /* GSM EFR DTX  */
-        TDMA_EFR_SID,       /* TDMA EFR DTX */
-        PDC_EFR_SID,        /* PDC EFR DTX  */
-        FOR_FUTURE_USE1,    /* Unused 1     */
-        FOR_FUTURE_USE2,    /* Unused 2     */
-        FOR_FUTURE_USE3,    /* Unused 3     */
-        AMR_NO_DATA         /* No data      */
-    };
-
     /*----------------------------------------------------------------------------
     ; STRUCTURES TYPEDEF'S
     ----------------------------------------------------------------------------*/
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index c4a8280..59dd740 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -132,6 +132,10 @@
             mInputBuffer->release();
             mInputBuffer = NULL;
         }
+
+        // Make sure that the next buffer output does not still
+        // depend on fragments from the last one decoded.
+        pvmp3_InitDecoder(mConfig, mDecoderBuf);
     } else {
         seekTimeUs = -1;
     }
diff --git a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
index 53f0638..703b41e 100644
--- a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VorbisDecoder"
+#include <utils/Log.h>
+
 #include "VorbisDecoder.h"
 
 #include <media/stagefright/MediaBufferGroup.h>
@@ -108,6 +112,7 @@
 
     mAnchorTimeUs = 0;
     mNumFramesOutput = 0;
+    mNumFramesLeftOnPage = 0;
     mStarted = true;
 
     return OK;
@@ -188,6 +193,13 @@
         }
     }
 
+    if (numFrames > mNumFramesLeftOnPage) {
+        LOGV("discarding %d frames at end of page",
+             numFrames - mNumFramesLeftOnPage);
+        numFrames = mNumFramesLeftOnPage;
+    }
+    mNumFramesLeftOnPage -= numFrames;
+
     out->set_range(0, numFrames * sizeof(int16_t) * mNumChannels);
 
     return numFrames;
@@ -226,6 +238,12 @@
         CHECK(seekTimeUs < 0);
     }
 
+    int32_t numPageSamples;
+    if (inputBuffer->meta_data()->findInt32(
+                kKeyValidSamples, &numPageSamples)) {
+        mNumFramesLeftOnPage = numPageSamples;
+    }
+
     MediaBuffer *outputBuffer;
     CHECK_EQ(mBufferGroup->acquire_buffer(&outputBuffer), OK);
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index ea2f7d5..db98253 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -93,6 +93,9 @@
     // This is a mask of MediaExtractor::Flags.
     uint32_t flags() const;
 
+    void postAudioEOS();
+    void postAudioSeekComplete();
+
 private:
     friend struct AwesomeEvent;
 
diff --git a/media/libstagefright/include/VorbisDecoder.h b/media/libstagefright/include/VorbisDecoder.h
index e9a488a..13e8b77 100644
--- a/media/libstagefright/include/VorbisDecoder.h
+++ b/media/libstagefright/include/VorbisDecoder.h
@@ -55,6 +55,7 @@
     int32_t mSampleRate;
     int64_t mAnchorTimeUs;
     int64_t mNumFramesOutput;
+    int32_t mNumFramesLeftOnPage;
 
     vorbis_dsp_state *mState;
     vorbis_info *mVi;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 9952783..47cca80 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -389,20 +389,23 @@
 
         // ES data follows.
 
-        onPayloadData(
-                PTS_DTS_flags, PTS, DTS,
-                br->data(), br->numBitsLeft() / 8);
-
         if (PES_packet_length != 0) {
             CHECK_GE(PES_packet_length, PES_header_data_length + 3);
 
             unsigned dataLength =
                 PES_packet_length - 3 - PES_header_data_length;
 
-            CHECK_EQ(br->numBitsLeft(), dataLength * 8);
+            CHECK_GE(br->numBitsLeft(), dataLength * 8);
+
+            onPayloadData(
+                    PTS_DTS_flags, PTS, DTS, br->data(), dataLength);
 
             br->skipBits(dataLength * 8);
         } else {
+            onPayloadData(
+                    PTS_DTS_flags, PTS, DTS,
+                    br->data(), br->numBitsLeft() / 8);
+
             size_t payloadSizeBits = br->numBitsLeft();
             CHECK((payloadSizeBits % 8) == 0);
 
@@ -491,7 +494,7 @@
     CHECK(picParamSet != NULL);
 
     buffer->setRange(stopOffset, size - stopOffset);
-    LOGI("buffer has %d bytes left.", buffer->size());
+    LOGV("buffer has %d bytes left.", buffer->size());
 
     size_t csdSize =
         1 + 3 + 1 + 1
@@ -527,6 +530,8 @@
     const uint8_t *data = *_data;
     size_t size = *_size;
 
+    // hexdump(data, size);
+
     *nalStart = NULL;
     *nalSize = 0;
 
@@ -572,18 +577,23 @@
         ++offset;
     }
 
-    CHECK_LT(offset + 2, size);
-
     *nalStart = &data[startOffset];
     *nalSize = endOffset - startOffset;
 
-    *_data = &data[offset];
-    *_size = size - offset;
+    if (offset + 2 < size) {
+        *_data = &data[offset];
+        *_size = size - offset;
+    } else {
+        *_data = NULL;
+        *_size = 0;
+    }
 
     return true;
 }
 
 sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) {
+    // hexdump(data, size);
+
     const uint8_t *tmpData = data;
     size_t tmpSize = size;
 
@@ -591,6 +601,7 @@
     const uint8_t *nalStart;
     size_t nalSize;
     while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) {
+        // hexdump(nalStart, nalSize);
         totalSize += 4 + nalSize;
     }
 
@@ -615,15 +626,15 @@
     CHECK_EQ(br.getBits(2), 0u);
     br.getBits(1);  // protection_absent
     unsigned profile = br.getBits(2);
-    LOGI("profile = %u", profile);
+    LOGV("profile = %u", profile);
     CHECK_NE(profile, 3u);
     unsigned sampling_freq_index = br.getBits(4);
     br.getBits(1);  // private_bit
     unsigned channel_configuration = br.getBits(3);
     CHECK_NE(channel_configuration, 0u);
 
-    LOGI("sampling_freq_index = %u", sampling_freq_index);
-    LOGI("channel_configuration = %u", channel_configuration);
+    LOGV("sampling_freq_index = %u", sampling_freq_index);
+    LOGV("channel_configuration = %u", channel_configuration);
 
     CHECK_LE(sampling_freq_index, 11u);
     static const int32_t kSamplingFreq[] = {
@@ -707,8 +718,8 @@
             sp<ABuffer> csd =
                 FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount);
 
-            LOGI("sampleRate = %d", sampleRate);
-            LOGI("channelCount = %d", channelCount);
+            LOGV("sampleRate = %d", sampleRate);
+            LOGV("channelCount = %d", channelCount);
 
             meta->setInt32(kKeySampleRate, sampleRate);
             meta->setInt32(kKeyChannelCount, channelCount);
@@ -716,7 +727,7 @@
             meta->setData(kKeyESDS, 0, csd->data(), csd->size());
         }
 
-        LOGI("created source!");
+        LOGV("created source!");
         mSource = new AnotherPacketSource(meta);
 
         // fall through
@@ -915,7 +926,10 @@
     unsigned adaptation_field_control = br->getBits(2);
     LOGV("adaptation_field_control = %u", adaptation_field_control);
 
-    MY_LOGV("continuity_counter = %u", br->getBits(4));
+    unsigned continuity_counter = br->getBits(4);
+    LOGV("continuity_counter = %u", continuity_counter);
+
+    // LOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter);
 
     if (adaptation_field_control == 2 || adaptation_field_control == 3) {
         parseAdaptationField(br);
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 56ca375..2417305 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -32,6 +32,8 @@
 
 namespace android {
 
+static const size_t kTSPacketSize = 188;
+
 struct MPEG2TSSource : public MediaSource {
     MPEG2TSSource(
             const sp<MPEG2TSExtractor> &extractor,
@@ -126,27 +128,37 @@
 void MPEG2TSExtractor::init() {
     bool haveAudio = false;
     bool haveVideo = false;
+    int numPacketsParsed = 0;
 
     while (feedMore() == OK) {
         ATSParser::SourceType type;
         if (haveAudio && haveVideo) {
             break;
         }
-        if (haveVideo) {
-            type = ATSParser::MPEG2ADTS_AUDIO;
-        } else {
-            type = ATSParser::AVC_VIDEO;
-        }
-        sp<AnotherPacketSource> impl =
-            (AnotherPacketSource *)mParser->getSource(type).get();
+        if (!haveVideo) {
+            sp<AnotherPacketSource> impl =
+                (AnotherPacketSource *)mParser->getSource(
+                        ATSParser::AVC_VIDEO).get();
 
-        if (impl != NULL) {
-            if (type == ATSParser::MPEG2ADTS_AUDIO) {
-                haveAudio = true;
-            } else {
+            if (impl != NULL) {
                 haveVideo = true;
+                mSourceImpls.push(impl);
             }
-            mSourceImpls.push(impl);
+        }
+
+        if (!haveAudio) {
+            sp<AnotherPacketSource> impl =
+                (AnotherPacketSource *)mParser->getSource(
+                        ATSParser::MPEG2ADTS_AUDIO).get();
+
+            if (impl != NULL) {
+                haveAudio = true;
+                mSourceImpls.push(impl);
+            }
+        }
+
+        if (++numPacketsParsed > 1500) {
+            break;
         }
     }
 
@@ -156,8 +168,6 @@
 status_t MPEG2TSExtractor::feedMore() {
     Mutex::Autolock autoLock(mLock);
 
-    static const size_t kTSPacketSize = 188;
-
     uint8_t packet[kTSPacketSize];
     ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
 
@@ -176,23 +186,18 @@
 bool SniffMPEG2TS(
         const sp<DataSource> &source, String8 *mimeType, float *confidence,
         sp<AMessage> *) {
-#if 0
-    char header;
-    if (source->readAt(0, &header, 1) != 1 || header != 0x47) {
-        return false;
+    for (int i = 0; i < 5; ++i) {
+        char header;
+        if (source->readAt(kTSPacketSize * i, &header, 1) != 1
+                || header != 0x47) {
+            return false;
+        }
     }
 
-    *confidence = 0.05f;
+    *confidence = 0.1f;
     mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
 
     return true;
-#else
-    // For now we're going to never identify this type of stream, since we'd
-    // just base our decision on a single byte...
-    // Instead you can instantiate an MPEG2TSExtractor by explicitly stating
-    // its proper mime type in the call to MediaExtractor::Create(...).
-    return false;
-#endif
 }
 
 }  // namespace android
diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp
index 865a294..35d90dc 100644
--- a/media/mtp/MtpCursor.cpp
+++ b/media/mtp/MtpCursor.cpp
@@ -399,8 +399,7 @@
 }
 
 
-bool MtpCursor::putLong(CursorWindow* window, int value, int row, int column) {
-
+bool MtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) {
     if (!window->putLong(row, column, value)) {
         window->freeLastRow();
         LOGE("Failed allocating space for a long in column %d", column);
diff --git a/media/mtp/MtpCursor.h b/media/mtp/MtpCursor.h
index 9e9833f..2e03c29 100644
--- a/media/mtp/MtpCursor.h
+++ b/media/mtp/MtpCursor.h
@@ -67,7 +67,7 @@
                         MtpObjectHandle objectID, int row);
 
     bool        prepareRow(CursorWindow* window);
-    bool        putLong(CursorWindow* window, int value, int row, int column);
+    bool        putLong(CursorWindow* window, int64_t value, int row, int column);
     bool        putString(CursorWindow* window, const char* text, int row, int column);
     bool        putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
                             MtpObjectFormat format, int row, int column);
diff --git a/native/android/Android.mk b/native/android/Android.mk
index cc35a3a..44ec83f 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -12,6 +12,7 @@
     looper.cpp \
     native_activity.cpp \
     native_window.cpp \
+    obb.cpp \
     sensor.cpp \
     storage_manager.cpp
 
diff --git a/native/android/obb.cpp b/native/android/obb.cpp
new file mode 100644
index 0000000..e0cb1a6
--- /dev/null
+++ b/native/android/obb.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NObb"
+
+#include <android/obb.h>
+
+#include <utils/Log.h>
+#include <utils/ObbFile.h>
+
+using namespace android;
+
+struct AObbInfo : public ObbFile {};
+
+AObbInfo* AObbScanner_getObbInfo(const char* filename) {
+    AObbInfo* obbFile = new AObbInfo();
+    if (obbFile == NULL || !obbFile->readFrom(filename)) {
+        delete obbFile;
+        return NULL;
+    }
+    obbFile->incStrong((void*)AObbScanner_getObbInfo);
+    return static_cast<AObbInfo*>(obbFile);
+}
+
+void AObbInfo_delete(AObbInfo* obbInfo) {
+    if (obbInfo != NULL) {
+        obbInfo->decStrong((void*)AObbScanner_getObbInfo);
+    }
+}
+
+const char* AObbInfo_getPackageName(AObbInfo* obbInfo) {
+    return obbInfo->getPackageName();
+}
+
+int32_t AObbInfo_getVersion(AObbInfo* obbInfo) {
+    return obbInfo->getVersion();
+}
+
+int32_t AObbInfo_getFlags(AObbInfo* obbInfo) {
+    return obbInfo->getFlags();
+}
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 6dbe746..2f20641 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -38,20 +38,20 @@
             mStorageManager(mgr)
     {}
 
-    virtual void onObbResult(const android::String16& filename, const android::String16& state) {
-        LOGD("Got obb result (%s, %s)\n", String8(filename).string(), String8(state).string());
-    }
+    virtual void onObbResult(const android::String16& filename, const android::String16& state);
 };
 
 struct AStorageManager : public RefBase {
 protected:
-    void* mObbCallback;
+    AStorageManager_obbCallbackFunc mObbCallback;
+    void* mObbCallbackData;
     sp<ObbActionListener> mObbActionListener;
     sp<IMountService> mMountService;
 
 public:
-    AStorageManager() :
-            mObbCallback(NULL)
+    AStorageManager()
+            : mObbCallback(NULL)
+            , mObbCallbackData(NULL)
     {
     }
 
@@ -73,8 +73,15 @@
         return true;
     }
 
-    void setObbCallback(void* cb) {
+    void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) {
         mObbCallback = cb;
+        mObbCallbackData = data;
+    }
+
+    void fireCallback(const char* filename, const char* state) {
+        if (mObbCallback != NULL) {
+            mObbCallback(filename, state, mObbCallbackData);
+        }
     }
 
     void mountObb(const char* filename, const char* key) {
@@ -85,7 +92,7 @@
 
     void unmountObb(const char* filename, const bool force) {
         String16 filename16(filename);
-        mMountService->unmountObb(filename16, force);
+        mMountService->unmountObb(filename16, force, mObbActionListener);
     }
 
     int isObbMounted(const char* filename) {
@@ -104,6 +111,10 @@
     }
 };
 
+void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) {
+    mStorageManager->fireCallback(String8(filename).string(), String8(state).string());
+}
+
 
 AStorageManager* AStorageManager_new() {
     sp<AStorageManager> mgr = new AStorageManager();
@@ -120,8 +131,8 @@
     }
 }
 
-void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb) {
-    mgr->setObbCallback(cb);
+void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) {
+    mgr->setObbCallback(cb, data);
 }
 
 void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
diff --git a/native/include/android/obb.h b/native/include/android/obb.h
new file mode 100644
index 0000000..65e9b2a
--- /dev/null
+++ b/native/include/android/obb.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_OBB_H
+#define ANDROID_OBB_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AObbInfo;
+typedef struct AObbInfo AObbInfo;
+
+enum {
+    AOBBINFO_OVERLAY = 0x0001,
+};
+
+/**
+ * Scan an OBB and get information about it.
+ */
+AObbInfo* AObbScanner_getObbInfo(const char* filename);
+
+/**
+ * Destroy the AObbInfo object. You must call this when finished with the object.
+ */
+void AObbInfo_delete(AObbInfo* obbInfo);
+
+/**
+ * Get the package name for the OBB.
+ */
+const char* AObbInfo_getPackageName(AObbInfo* obbInfo);
+
+/**
+ * Get the version of an OBB file.
+ */
+int32_t AObbInfo_getVersion(AObbInfo* obbInfo);
+
+/**
+ * Get the flags of an OBB file.
+ */
+int32_t AObbInfo_getFlags(AObbInfo* obbInfo);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif      // ANDROID_OBB_H
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
index bbed8a4..6f925c1 100644
--- a/native/include/android/storage_manager.h
+++ b/native/include/android/storage_manager.h
@@ -37,17 +37,22 @@
 void AStorageManager_delete(AStorageManager* mgr);
 
 /**
- * Callback to call when requested OBB is complete.
+ * Callback function for asynchronous calls made on OBB files.
  */
-void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb);
+typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data);
 
 /**
- * Attempts to mount an OBB file.
+ * Callback to call when requested asynchronous OBB operation is complete.
+ */
+void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data);
+
+/**
+ * Attempts to mount an OBB file. This is an asynchronous operation.
  */
 void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key);
 
 /**
- * Attempts to unmount an OBB file.
+ * Attempts to unmount an OBB file. This is an asynchronous operation.
  */
 void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force);
 
@@ -66,4 +71,4 @@
 };
 #endif
 
-#endif      // ANDROID_PACKAGE_MANAGER_H
+#endif      // ANDROID_STORAGE_MANAGER_H
diff --git a/opengl/tests/testFramerate/Android.mk b/opengl/tests/testFramerate/Android.mk
new file mode 100644
index 0000000..500abf3
--- /dev/null
+++ b/opengl/tests/testFramerate/Android.mk
@@ -0,0 +1,19 @@
+#########################################################################
+# Test framerate and look for hiccups
+#########################################################################
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestFramerate
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testFramerate/AndroidManifest.xml b/opengl/tests/testFramerate/AndroidManifest.xml
new file mode 100644
index 0000000..e04342c
--- /dev/null
+++ b/opengl/tests/testFramerate/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testframerate">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+    <application
+            android:label="@string/testFramerate_activity">
+        <activity android:name="TestFramerateActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:launchMode="singleTask"
+                android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testFramerate/res/values/strings.xml b/opengl/tests/testFramerate/res/values/strings.xml
new file mode 100644
index 0000000..e6b3088
--- /dev/null
+++ b/opengl/tests/testFramerate/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="testFramerate_activity">TestFramerate</string>
+
+</resources>
+
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
new file mode 100644
index 0000000..cbe279b
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testframerate;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class TestFramerateActivity extends Activity {
+
+    TestFramerateView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestFramerateView(getApplication());
+        setContentView(mView);
+        mView.setFocusableInTouchMode(true);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
new file mode 100644
index 0000000..f3fb5de
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testframerate;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLES20;
+
+class TestFramerateView extends GLSurfaceView {
+    private static String TAG = "TestFramerateView";
+
+    public TestFramerateView(Context context) {
+        super(context);
+        setEGLContextClientVersion(2);
+        setRenderer(new Renderer());
+    }
+
+    private long mLastTime_us = 0;
+    private long mNumShortFramesElapsed = 0;
+    private void registerTime(long now_us) {
+        long longFrameTime_ms = Integer.parseInt(SystemProperties.get("debug.longframe_ms", "16"));
+        long elapsedTime_us = now_us - mLastTime_us;
+        float fps = 1000000.f / elapsedTime_us;
+        if (mLastTime_us > 0 && elapsedTime_us > longFrameTime_ms*1000) {
+          Log.v(TAG, "Long frame: " + elapsedTime_us/1000.f + " ms (" + fps + " fps)");
+          if (mNumShortFramesElapsed > 0) {
+            Log.v(TAG, "  Short frames since last long frame: " + mNumShortFramesElapsed);
+            mNumShortFramesElapsed = 0;
+          }
+        } else {
+            ++mNumShortFramesElapsed;
+        }
+
+        mLastTime_us = now_us;
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        public Renderer() {
+        }
+
+
+        public void onDrawFrame(GL10 gl) {
+            long now_us = System.nanoTime() / 1000;
+            registerTime(now_us);
+
+            float red = (now_us % 1000000) / 1000000.f;
+            float green = (now_us % 2000000) / 2000000.f;
+            float blue = (now_us % 3000000) / 3000000.f;
+            GLES20.glClearColor(red, green, blue, 1.0f);
+            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        }
+
+    }
+}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index f08bd3c..eb86277 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -156,7 +156,12 @@
         }
 
         public ObbInfo getObbInfo(String filename) {
-            return ObbScanner.getObbInfo(filename);
+            try {
+                return ObbScanner.getObbInfo(filename);
+            } catch (IOException e) {
+                Log.d(TAG, "Couldn't get OBB info", e);
+                return null;
+            }
         }
     };
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f1f31cf..6057023 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -32,6 +32,7 @@
         <activity android:name=".recent.RecentApplicationsActivity"
             android:theme="@android:style/Theme.NoTitleBar"
             android:excludeFromRecents="true"
+            android:launchMode="singleInstance"
             android:exported="true">
         </activity>
 
diff --git a/packages/SystemUI/res/anim/navigation_in.xml b/packages/SystemUI/res/anim/navigation_in.xml
new file mode 100644
index 0000000..630fd72
--- /dev/null
+++ b/packages/SystemUI/res/anim/navigation_in.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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/navigation_out.xml b/packages/SystemUI/res/anim/navigation_out.xml
new file mode 100644
index 0000000..4717e47
--- /dev/null
+++ b/packages/SystemUI/res/anim/navigation_out.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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/recent_app_enter.xml b/packages/SystemUI/res/anim/recent_app_enter.xml
new file mode 100644
index 0000000..4947eee
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+     it starts at 200% and scales down.  Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator">
+    <scale android:fromXScale="0.25" android:toXScale="1.0"
+           android:fromYScale="0.25" android:toYScale="1.0"
+           android:pivotX="0%p" android:pivotY="0%p"
+           android:duration="500" />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+            android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recent_app_leave.xml b/packages/SystemUI/res/anim/recent_app_leave.xml
new file mode 100644
index 0000000..3d83988
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_leave.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+     it starts at 200% and scales down.  Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator">
+    <scale android:fromXScale="1.0" android:toXScale="0.25"
+           android:fromYScale="1.0" android:toYScale="0.25"
+           android:pivotX="0%p" android:pivotY="0%p"
+           android:duration="500" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+            android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/anim/system_in.xml b/packages/SystemUI/res/anim/system_in.xml
new file mode 100644
index 0000000..630fd72
--- /dev/null
+++ b/packages/SystemUI/res/anim/system_in.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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/system_out.xml b/packages/SystemUI/res/anim/system_out.xml
new file mode 100644
index 0000000..4717e47
--- /dev/null
+++ b/packages/SystemUI/res/anim/system_out.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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png
index f9a7cc2..3b7c9c7 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png
index 4461ac8..3b7c9c7 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recent_overlay.png b/packages/SystemUI/res/drawable/recent_overlay.png
new file mode 100644
index 0000000..4dfa3d9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_overlay.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recent_rez_border.png b/packages/SystemUI/res/drawable/recent_rez_border.png
new file mode 100644
index 0000000..ad025f5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_rez_border.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 5741a66..6aee011 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -119,46 +119,50 @@
             android:layout_centerInParent="true"
             />
 
-        <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_toLeftOf="@+id/recent"
-            android:src="@drawable/ic_sysbar_menu"
-            android:background="@drawable/ic_sysbar_icon_bg"
-            android:paddingLeft="4dip"
-            android:paddingRight="4dip"
-            systemui:keyCode="82"
-            />
-        <ImageButton android:id="@+id/recent"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_toLeftOf="@+id/home"
-            android:src="@drawable/ic_sysbar_recent"
-            android:background="@drawable/ic_sysbar_icon_bg"
-            android:paddingLeft="4dip"
-            android:paddingRight="4dip"
-            android:onClick="recentButtonClicked"
-            />
-        <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_toLeftOf="@+id/back"
-            android:paddingLeft="4dip"
-            android:paddingRight="4dip"
-            android:src="@drawable/ic_sysbar_home"
-            android:background="@drawable/ic_sysbar_icon_bg"
-            systemui:keyCode="3"
-            />
-        <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
+        <LinearLayout
+            android:id="@+id/navigationArea"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_alignParentRight="true"
-            android:paddingLeft="4dip"
-            android:paddingRight="4dip"
-            android:src="@drawable/ic_sysbar_back"
-            android:background="@drawable/ic_sysbar_icon_bg"
-            systemui:keyCode="4"
-            />
+            android:orientation="horizontal"
+            >
+            <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:src="@drawable/ic_sysbar_menu"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                android:paddingLeft="4dip"
+                android:paddingRight="4dip"
+                systemui:keyCode="82"
+                />
+            <ImageButton android:id="@+id/recent"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:src="@drawable/ic_sysbar_recent"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                android:paddingLeft="4dip"
+                android:paddingRight="4dip"
+                android:onClick="recentButtonClicked"
+                />
+            <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingLeft="4dip"
+                android:paddingRight="4dip"
+                android:src="@drawable/ic_sysbar_home"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                systemui:keyCode="3"
+                />
+            <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingLeft="4dip"
+                android:paddingRight="4dip"
+                android:src="@drawable/ic_sysbar_back"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                systemui:keyCode="4"
+                />
+        </LinearLayout>
     </RelativeLayout>
 
     <!-- It's curtains for you. -->
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml b/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
index fa492d0..049a1cc 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
@@ -8,12 +8,14 @@
     <ImageButton
         android:id="@+id/veto"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="match_parent"
         android:layout_centerVertical="true"
         android:layout_alignParentRight="true"
         android:src="@drawable/status_bar_veto"
+        android:scaleType="center"
         android:background="@null"
-        android:padding="2dip"
+        android:paddingLeft="16dip"
+        android:paddingRight="16dip"
         />
 
     <com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
diff --git a/packages/SystemUI/res/layout/recents_detail_view.xml b/packages/SystemUI/res/layout/recents_detail_view.xml
new file mode 100644
index 0000000..879d0f2
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_detail_view.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <!-- Application Title -->
+    <TextView android:id="@+id/app_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:singleLine="true"/>
+
+    <!-- Application Details -->
+    <TextView
+        android:id="@+id/app_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index 9cc24be..bf24a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -20,7 +20,9 @@
 import com.android.systemui.R;
 
 import com.android.ex.carousel.CarouselView;
+import com.android.ex.carousel.CarouselViewHelper;
 import com.android.ex.carousel.CarouselRS.CarouselCallback;
+import com.android.ex.carousel.CarouselViewHelper.DetailTextureParameters;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -38,37 +40,104 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.PorterDuff;
 import android.graphics.Bitmap.Config;
 import android.graphics.drawable.Drawable;
 import android.graphics.PixelFormat;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.TextView;
 
 public class RecentApplicationsActivity extends Activity {
     private static final String TAG = "RecentApplicationsActivity";
-    private static boolean DBG = true;
+    private static boolean DBG = false;
     private static final int CARD_SLOTS = 56;
     private static final int VISIBLE_SLOTS = 7;
     private static final int MAX_TASKS = VISIBLE_SLOTS * 2;
+
+    // TODO: these should be configurable
+    private static final int DETAIL_TEXTURE_MAX_WIDTH = 200;
+    private static final int DETAIL_TEXTURE_MAX_HEIGHT = 80;
+    private static final int TEXTURE_WIDTH = 256;
+    private static final int TEXTURE_HEIGHT = 256;
+
     private ActivityManager mActivityManager;
     private List<RunningTaskInfo> mRunningTaskList;
     private boolean mPortraitMode = true;
     private ArrayList<ActivityDescription> mActivityDescriptions
             = new ArrayList<ActivityDescription>();
     private CarouselView mCarouselView;
+    private LocalCarouselViewHelper mHelper;
     private View mNoRecentsView;
-    private Bitmap mBlankBitmap = Bitmap.createBitmap(
-            new int[] {0xff808080, 0xffffffff, 0xff808080, 0xffffffff}, 2, 2, Config.RGB_565);
+    private Bitmap mLoadingBitmap;
+    private Bitmap mRecentOverlay;
+    private boolean mHidden = false;
+    private boolean mHiding = false;
+    private DetailInfo mDetailInfo;
+
+    /**
+     * This class is a container for all items associated with the DetailView we'll
+     * be drawing to a bitmap and sending to Carousel.
+     *
+     */
+    static final class DetailInfo {
+        public DetailInfo(View _view, TextView _title, TextView _desc) {
+            view = _view;
+            title = _title;
+            description = _desc;
+        }
+
+        /**
+         * Draws view into the given bitmap, if provided
+         * @param bitmap
+         */
+        public Bitmap draw(Bitmap bitmap) {
+            resizeView(view, DETAIL_TEXTURE_MAX_WIDTH, DETAIL_TEXTURE_MAX_HEIGHT);
+            int desiredWidth = view.getWidth();
+            int desiredHeight = view.getHeight();
+            if (bitmap == null || desiredWidth != bitmap.getWidth()
+                    || desiredHeight != bitmap.getHeight()) {
+                bitmap = Bitmap.createBitmap(desiredWidth, desiredHeight, Config.ARGB_8888);
+            }
+            Canvas canvas = new Canvas(bitmap);
+            view.draw(canvas);
+            return bitmap;
+        }
+
+        /**
+         * Force a layout pass on the given view.
+         */
+        private void resizeView(View view, int maxWidth, int maxHeight) {
+            int widthSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+                    | MeasureSpec.getSize(maxWidth);
+            int heightSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+                    | MeasureSpec.getSize(maxHeight);
+            view.measure(widthSpec, heightSpec);
+            view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+            Log.v(TAG, "RESIZED VIEW: " + view.getWidth() + ", " + view.getHeight());
+        }
+
+        public View view;
+        public TextView title;
+        public TextView description;
+    }
 
     static class ActivityDescription {
         int id;
         Bitmap thumbnail; // generated by Activity.onCreateThumbnail()
         Drawable icon; // application package icon
         String label; // application package label
-        String description; // generated by Activity.onCreateDescription()
+        CharSequence description; // generated by Activity.onCreateDescription()
         Intent intent; // launch intent for application
         Matrix matrix; // arbitrary rotation matrix to correct orientation
         int position; // position in list
@@ -106,14 +175,17 @@
         return null;
     }
 
-    final CarouselCallback mCarouselCallback = new CarouselCallback() {
+    private class LocalCarouselViewHelper extends CarouselViewHelper {
+        private Paint mPaint = new Paint();
+        private DetailTextureParameters mDetailParams = new DetailTextureParameters(10.0f, 20.0f);
 
-        public void onAnimationFinished() {
-
+        public LocalCarouselViewHelper(Context context) {
+            super(context);
         }
 
-        public void onAnimationStarted() {
-
+        @Override
+        public DetailTextureParameters getDetailTextureParameters(int id) {
+            return mDetailParams;
         }
 
         public void onCardSelected(int n) {
@@ -125,7 +197,7 @@
                     try {
                         if (DBG) Log.v(TAG, "Starting intent " + item.intent);
                         startActivity(item.intent);
-                        //overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
+                        overridePendingTransition(R.anim.recent_app_enter, R.anim.recent_app_leave);
                     } catch (ActivityNotFoundException e) {
                         if (DBG) Log.w("Recent", "Unable to launch recent task", e);
                     }
@@ -134,49 +206,89 @@
             }
         }
 
-        public void onInvalidateTexture(int n) {
-
-        }
-
-        public void onRequestGeometry(int n) {
-
-        }
-
-        public void onInvalidateGeometry(int n) {
-
-        }
-
-        public void onRequestTexture(final int n) {
-            if (DBG) Log.v(TAG, "onRequestTexture(" + n + ")");
-            if (n < mActivityDescriptions.size()) {
-                mCarouselView.post(new Runnable() {
-                    public void run() {
-                        ActivityDescription info = mActivityDescriptions.get(n);
-                        if (info != null) {
-                            if (DBG) Log.v(TAG, "FOUND ACTIVITY THUMBNAIL " + info.thumbnail);
-                            Bitmap bitmap = info.thumbnail == null ? mBlankBitmap : info.thumbnail;
-                            mCarouselView.setTextureForItem(n, bitmap);
-                        } else {
-                            if (DBG) Log.v(TAG, "FAILED TO GET ACTIVITY THUMBNAIL FOR ITEM " + n);
-                        }
-                    }
-                });
+        @Override
+        public Bitmap getTexture(final int id) {
+            if (DBG) Log.v(TAG, "onRequestTexture(" + id + ")");
+            ActivityDescription info;
+            synchronized(mActivityDescriptions) {
+                info = mActivityDescriptions.get(id);
             }
+            Bitmap bitmap = null;
+            if (info != null) {
+                bitmap = compositeBitmap(info);
+            }
+            return bitmap;
         }
 
-        public void onInvalidateDetailTexture(int n) {
-
-        }
-
-        public void onRequestDetailTexture(int n) {
-
-        }
-
-        public void onReportFirstCardPosition(int n) {
-
+        @Override
+        public Bitmap getDetailTexture(int n) {
+            Bitmap bitmap = null;
+            if (n < mActivityDescriptions.size()) {
+                ActivityDescription item = mActivityDescriptions.get(n);
+                mDetailInfo.title.setText(item.label);
+                mDetailInfo.description.setText(item.description);
+                bitmap = mDetailInfo.draw(null);
+            }
+            return bitmap;
         }
     };
 
+    private Bitmap compositeBitmap(ActivityDescription info) {
+        final int targetWidth = TEXTURE_WIDTH;
+        final int targetHeight = TEXTURE_HEIGHT;
+        final int border = 3; // inset along the edge for thumnnail content
+        final int overlap = 1; // how many pixels of overlap between border and thumbnail
+        final Resources res = getResources();
+        if (mRecentOverlay == null) {
+            mRecentOverlay = BitmapFactory.decodeResource(res, R.drawable.recent_overlay);
+        }
+
+        // Create a bitmap of the proper size/format and set the canvas to draw to it
+        final Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(result);
+        canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG));
+        Paint paint = new Paint();
+        paint.setFilterBitmap(false);
+
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+        canvas.save();
+        if (info.thumbnail != null) {
+            // Draw the thumbnail
+            int sourceWidth = targetWidth - 2 * (border - overlap);
+            int sourceHeight = targetHeight - 2 * (border - overlap);
+            final float scaleX = (float) sourceWidth / info.thumbnail.getWidth();
+            final float scaleY = (float) sourceHeight / info.thumbnail.getHeight();
+            canvas.translate(border * 0.5f, border * 0.5f);
+            canvas.scale(scaleX, scaleY);
+            canvas.drawBitmap(info.thumbnail, 0, 0, paint);
+        } else {
+            // Draw the Loading bitmap placeholder, TODO: Remove when RS handles blending
+            final float scaleX = (float) targetWidth / mLoadingBitmap.getWidth();
+            final float scaleY = (float) targetHeight / mLoadingBitmap.getHeight();
+            canvas.scale(scaleX, scaleY);
+            canvas.drawBitmap(mLoadingBitmap, 0, 0, paint);
+        }
+        canvas.restore();
+
+        // Draw overlay
+        canvas.save();
+        final float scaleOverlayX = (float) targetWidth / mRecentOverlay.getWidth();
+        final float scaleOverlayY = (float) targetHeight / mRecentOverlay.getHeight();
+        canvas.scale(scaleOverlayX, scaleOverlayY);
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+        canvas.drawBitmap(mRecentOverlay, 0, 0, paint);
+        canvas.restore();
+
+        // Draw icon
+        if (info.icon != null) {
+            canvas.save();
+            info.icon.draw(canvas);
+            canvas.restore();
+        }
+
+        return result;
+    }
+
     private final IThumbnailReceiver mThumbnailReceiver = new IThumbnailReceiver.Stub() {
 
         public void finished() throws RemoteException {
@@ -192,6 +304,7 @@
             ActivityDescription info = findActivityDescription(id);
             if (info != null) {
                 info.thumbnail = bitmap;
+                info.description = description;
                 final int thumbWidth = bitmap.getWidth();
                 final int thumbHeight = bitmap.getHeight();
                 if ((mPortraitMode && thumbWidth > thumbHeight)
@@ -202,13 +315,34 @@
                 } else {
                     info.matrix = null;
                 }
-                mCarouselView.setTextureForItem(info.position, info.thumbnail);
+                mCarouselView.setTextureForItem(info.position, compositeBitmap(info));
             } else {
                 if (DBG) Log.v(TAG, "Can't find view for id " + id);
             }
         }
     };
 
+    /**
+     * We never really finish() RecentApplicationsActivity, since we don't want to
+     * get destroyed and pay the start-up cost to restart it.
+     */
+    @Override
+    public void finish() {
+        moveTaskToBack(true);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        mHidden = !mHidden;
+        if (mHidden) {
+            mHiding = true;
+            moveTaskToBack(true);
+        } else {
+            mHiding = false;
+        }
+        super.onNewIntent(intent);
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -218,25 +352,35 @@
 
         getWindow().getDecorView().setBackgroundColor(0x80000000);
         setContentView(R.layout.recent_apps_activity);
-        mCarouselView = (CarouselView)findViewById(R.id.carousel);
-        mNoRecentsView = (View) findViewById(R.id.no_applications_message);
-        //mCarouselView = new CarouselView(this);
-        //setContentView(mCarouselView);
-        mCarouselView.setSlotCount(CARD_SLOTS);
-        mCarouselView.setVisibleSlots(VISIBLE_SLOTS);
-        mCarouselView.createCards(1);
-        mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
-        mCarouselView.setDefaultBitmap(mBlankBitmap);
-        mCarouselView.setLoadingBitmap(mBlankBitmap);
-        mCarouselView.setCallback(mCarouselCallback);
-        mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
-
-        mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-        mPortraitMode = decorView.getHeight() > decorView.getWidth();
-
-        refresh();
 
 
+        if (mCarouselView == null) {
+            mLoadingBitmap = BitmapFactory.decodeResource(res, R.drawable.recent_rez_border);
+            mCarouselView = (CarouselView)findViewById(R.id.carousel);
+            mHelper = new LocalCarouselViewHelper(this);
+            mHelper.setCarouselView(mCarouselView);
+
+            mCarouselView.setSlotCount(CARD_SLOTS);
+            mCarouselView.setVisibleSlots(VISIBLE_SLOTS);
+            mCarouselView.createCards(0);
+            mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
+            mCarouselView.setDefaultBitmap(mLoadingBitmap);
+            mCarouselView.setLoadingBitmap(mLoadingBitmap);
+            mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+
+            mNoRecentsView = (View) findViewById(R.id.no_applications_message);
+
+            mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+            mPortraitMode = decorView.getHeight() > decorView.getWidth();
+
+            // Load detail view which will be used to render text
+            View detail = getLayoutInflater().inflate(R.layout.recents_detail_view, null);
+            TextView title = (TextView) detail.findViewById(R.id.app_title);
+            TextView description = (TextView) detail.findViewById(R.id.app_description);
+            mDetailInfo = new DetailInfo(detail, title, description);
+
+            refresh();
+        }
     }
 
     @Override
@@ -264,7 +408,7 @@
                 ActivityDescription desc = findActivityDescription(r.id);
                 if (desc != null) {
                     desc.thumbnail = r.thumbnail;
-                    desc.label = r.topActivity.flattenToShortString();
+                    desc.description = r.description;
                     if ((mPortraitMode && thumbWidth > thumbHeight)
                             || (!mPortraitMode && thumbWidth < thumbHeight)) {
                         Matrix matrix = new Matrix();
@@ -336,17 +480,34 @@
         }
     }
 
-    private void refresh() {
-        updateRecentTasks();
-        updateRunningTasks();
-        if (mActivityDescriptions.size() == 0) {
-            // show "No Recent Takss"
-            mNoRecentsView.setVisibility(View.VISIBLE);
-            mCarouselView.setVisibility(View.GONE);
-        } else {
+    private final Runnable mRefreshRunnable = new Runnable() {
+        public void run() {
+            updateRecentTasks();
+            updateRunningTasks();
+            showCarousel(mActivityDescriptions.size() > 0);
+        }
+    };
+
+    private void showCarousel(boolean show) {
+        if (show) {
+            // Make carousel visible
             mNoRecentsView.setVisibility(View.GONE);
             mCarouselView.setVisibility(View.VISIBLE);
             mCarouselView.createCards(mActivityDescriptions.size());
+        } else {
+            // show "No Recent Tasks"
+            mNoRecentsView.setVisibility(View.VISIBLE);
+            mCarouselView.setVisibility(View.GONE);
+        }
+    }
+
+    private void refresh() {
+        if (!mHiding && mCarouselView != null) {
+            // Don't update the view now. Instead, post a request so it happens next time
+            // we reach the looper after a delay. This way we can fold multiple refreshes
+            // into just the latest.
+            mCarouselView.removeCallbacks(mRefreshRunnable);
+            mCarouselView.postDelayed(mRefreshRunnable, 50);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
index d89d093..31b78b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
@@ -99,7 +99,7 @@
         }
         if (showSpn && spn != null) {
             if (something) {
-                str.append(' ');
+                str.append('\n');
             }
             str.append(spn);
             something = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3ef12f9..81091e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -175,8 +175,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/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 45abe93..7e8a5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -117,7 +117,7 @@
     public boolean hasClearableItems() {
         for (Entry e : mEntries) {
             if (e.expanded != null) { // the view successfully inflated
-                if ((e.notification.notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
+                if (e.notification.isClearable()) {
                     return true;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 0309430..e0019b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -18,9 +18,8 @@
 
 import android.app.StatusBarManager;
 import android.app.AlertDialog;
-import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothPbap;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -319,9 +318,6 @@
     private boolean mVolumeVisible;
 
     // bluetooth device status
-    private int mBluetoothHeadsetState;
-    private boolean mBluetoothA2dpConnected;
-    private int mBluetoothPbapState;
     private boolean mBluetoothEnabled;
 
     // wifi
@@ -369,9 +365,7 @@
                 onBatteryOkay(intent);
             }
             else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
-                    action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
-                    action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
-                    action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+                    action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
                 updateBluetooth(intent);
             }
             else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
@@ -454,9 +448,6 @@
         } else {
             mBluetoothEnabled = false;
         }
-        mBluetoothA2dpConnected = false;
-        mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
-        mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
         mService.setIconVisibility("bluetooth", mBluetoothEnabled);
 
         // Gps status
@@ -490,9 +481,7 @@
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
-        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
+        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
@@ -1064,28 +1053,16 @@
         if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
             mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
-        } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-            mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                    BluetoothHeadset.STATE_ERROR);
-        } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-            BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-            if (a2dp.getConnectedSinks().size() != 0) {
-                mBluetoothA2dpConnected = true;
-            } else {
-                mBluetoothA2dpConnected = false;
+        } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
+            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                BluetoothAdapter.STATE_DISCONNECTED);
+            if (state == BluetoothAdapter.STATE_CONNECTED) {
+                iconId = R.drawable.stat_sys_data_bluetooth_connected;
             }
-        } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
-            mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
-                    BluetoothPbap.STATE_DISCONNECTED);
         } else {
             return;
         }
 
-        if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
-                mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
-            iconId = R.drawable.stat_sys_data_bluetooth_connected;
-        }
-
         mService.setIcon("bluetooth", iconId, 0);
         mService.setIconVisibility("bluetooth", mBluetoothEnabled);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 49337fde..0e26f52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -78,6 +78,7 @@
     NotificationIconArea mNotificationIconArea;
     View mNotificationButtons;
     View mSystemInfo;
+    View mNavigationArea;
 
     NotificationPanel mNotificationPanel;
     SystemPanel mSystemPanel;
@@ -98,7 +99,6 @@
     TabletTicker mTicker;
     View mTickerView;
     boolean mTicking;
-    boolean mExpandedVisible;
 
     // for disabling the status bar
     int mDisabled = 0;
@@ -203,6 +203,12 @@
         mSignalMeter = (ImageView) sb.findViewById(R.id.signal);
         mSignalIcon = (ImageView) sb.findViewById(R.id.signal_icon);
 
+        // The navigation buttons
+        mNavigationArea = sb.findViewById(R.id.navigationArea);
+
+        // set the initial view visibility
+        setAreThereNotifications();
+
         // Add the windows
         addPanelWindows();
 
@@ -224,24 +230,26 @@
             switch (m.what) {
                 case MSG_OPEN_NOTIFICATION_PANEL:
                     if (DEBUG) Slog.d(TAG, "opening notifications panel");
-                    mDoNotDisturbButton.setText(mNotificationsOn
-                            ? R.string.status_bar_do_not_disturb_button
-                            : R.string.status_bar_please_disturb_button);
-                    mNotificationPanel.setVisibility(View.VISIBLE);
-                    mNotificationIconArea.setAnimation(loadAnim(R.anim.notification_icons_out));
-                    mNotificationIconArea.setVisibility(View.GONE);
-                    mNotificationButtons.setAnimation(loadAnim(R.anim.notification_icons_in));
-                    mNotificationButtons.setVisibility(View.VISIBLE);
-                    mExpandedVisible = true;
+                    if (mNotificationPanel.getVisibility() == View.GONE) {
+                        mDoNotDisturbButton.setText(mNotificationsOn
+                                ? R.string.status_bar_do_not_disturb_button
+                                : R.string.status_bar_please_disturb_button);
+                        mNotificationPanel.setVisibility(View.VISIBLE);
+                        setViewVisibility(mNotificationIconArea, View.GONE,
+                                R.anim.notification_icons_out);
+                        setViewVisibility(mNotificationButtons, View.VISIBLE,
+                                R.anim.notification_buttons_in);
+                    }
                     break;
                 case MSG_CLOSE_NOTIFICATION_PANEL:
                     if (DEBUG) Slog.d(TAG, "closing notifications panel");
-                    mNotificationPanel.setVisibility(View.GONE);
-                    mNotificationIconArea.setAnimation(loadAnim(R.anim.notification_icons_in));
-                    mNotificationIconArea.setVisibility(View.VISIBLE);
-                    mNotificationButtons.setAnimation(loadAnim(R.anim.notification_buttons_out));
-                    mNotificationButtons.setVisibility(View.GONE);
-                    mExpandedVisible = false;
+                    if (mNotificationPanel.getVisibility() == View.VISIBLE) {
+                        mNotificationPanel.setVisibility(View.GONE);
+                        setViewVisibility(mNotificationIconArea, View.VISIBLE,
+                                R.anim.notification_icons_in);
+                        setViewVisibility(mNotificationButtons, View.GONE,
+                                R.anim.notification_buttons_out);
+                    }
                     break;
                 case MSG_OPEN_SYSTEM_PANEL:
                     if (DEBUG) Slog.d(TAG, "opening system panel");
@@ -313,6 +321,8 @@
         } else {
             tick(notification);
         }
+
+        setAreThereNotifications();
     }
 
     public void updateNotification(IBinder key, StatusBarNotification notification) {
@@ -382,19 +392,25 @@
             addNotificationViews(key, notification);
         }
         // TODO: ticker; immersive mode
+
+        setAreThereNotifications();
     }
 
     public void removeNotification(IBinder key) {
         if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ") // TODO");
         removeNotificationViews(key);
+        setAreThereNotifications();
     }
 
     public void disable(int state) {
-        /*
-        final int old = mDisabled;
-        final int diff = state ^ old;
+        int old = mDisabled;
+        int diff = state ^ old;
+        Slog.d(TAG, "disable... old=0x" + Integer.toHexString(old)
+                + " diff=0x" + Integer.toHexString(diff)
+                + " state=0x" + Integer.toHexString(state));
         mDisabled = state;
 
+        // act accordingly
         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
                 Slog.d(TAG, "DISABLE_EXPAND: yes");
@@ -404,60 +420,41 @@
         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
-                if (mTicking) {
-                    mTicker.halt();
-                } else {
-                    mNotificationIconArea.setVisibility(View.INVISIBLE);
-                }
+                setViewVisibility(mNotificationTrigger, View.GONE,
+                        R.anim.notification_icons_out);
+                setViewVisibility(mNotificationIconArea, View.GONE,
+                        R.anim.notification_icons_out);
+                mTicker.halt();
             } else {
                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
-                if (!mExpandedVisible) {
-                    mNotificationIconArea.setVisibility(View.VISIBLE);
-                }
+                setViewVisibility(mNotificationTrigger, View.VISIBLE,
+                        R.anim.notification_icons_in);
+                setViewVisibility(mNotificationIconArea, View.VISIBLE,
+                        R.anim.notification_icons_in);
             }
         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+            if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
                 mTicker.halt();
             }
         }
-        */
-    }
-
-    void performDisableActions(int net) {
-        /*
-        int old = mDisabled;
-        int diff = net ^ old;
-        mDisabled = net;
-
-        // act accordingly
-        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
-            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
-                Slog.d(TAG, "DISABLE_EXPAND: yes");
-                animateCollapse();
-            }
-        }
-        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
-                if (mTicking) {
-                    mNotificationIconArea.setVisibility(View.INVISIBLE);
-                    mTicker.halt();
-                } else {
-                    mNotificationIconArea.setVisibility(View.INVISIBLE);
-                }
+        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+                Slog.d(TAG, "DISABLE_SYSTEM_INFO: yes");
+                setViewVisibility(mSystemInfo, View.GONE, R.anim.navigation_out);
             } else {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
-                if (!mExpandedVisible) {
-                    mNotificationIconArea.setVisibility(View.VISIBLE);
-                }
-            }
-        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                mTicker.halt();
+                Slog.d(TAG, "DISABLE_SYSTEM_INFO: no");
+                setViewVisibility(mSystemInfo, View.VISIBLE, R.anim.navigation_in);
             }
         }
-        */
+        if ((diff & StatusBarManager.DISABLE_NAVIGATION) != 0) {
+            if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) {
+                Slog.d(TAG, "DISABLE_NAVIGATION: yes");
+                setViewVisibility(mNavigationArea, View.GONE, R.anim.navigation_out);
+            } else {
+                Slog.d(TAG, "DISABLE_NAVIGATION: no");
+                setViewVisibility(mNavigationArea, View.VISIBLE, R.anim.navigation_in);
+            }
+        }
     }
 
     private boolean hasTicker(Notification n) {
@@ -497,35 +494,70 @@
 
     public void setLightsOn(boolean on) {
         if (on) {
-            mCurtains.setAnimation(loadAnim(R.anim.lights_out_out));
-            mCurtains.setVisibility(View.GONE);
-            mBarContents.setAnimation(loadAnim(R.anim.status_bar_in));
-            mBarContents.setVisibility(View.VISIBLE);
+            setViewVisibility(mCurtains, View.GONE, R.anim.lights_out_out);
+            setViewVisibility(mBarContents, View.VISIBLE, R.anim.status_bar_in);
         } else {
             animateCollapse();
-            mCurtains.setAnimation(loadAnim(R.anim.lights_out_in));
-            mCurtains.setVisibility(View.VISIBLE);
-            mBarContents.setAnimation(loadAnim(R.anim.status_bar_out));
-            mBarContents.setVisibility(View.GONE);
+            setViewVisibility(mCurtains, View.VISIBLE, R.anim.lights_out_in);
+            setViewVisibility(mBarContents, View.GONE, R.anim.status_bar_out);
         }
     }
 
+    private void setAreThereNotifications() {
+        final boolean hasClearable = mNotns.hasClearableItems();
+
+        //Slog.d(TAG, "setAreThereNotifications hasClerable=" + hasClearable);
+
+        // Show or hide the "Clear all" button.  Note that we don't do an animation
+        // if it's not on screen, so that if someone opens the bar right then they
+        // don't see the animation in progress.
+        // (no ongoing notifications are clearable)
+        if (hasClearable) {
+            if (mNotificationButtons.getVisibility() == View.VISIBLE) {
+                setViewVisibility(mClearButton, View.VISIBLE, R.anim.notification_buttons_in);
+            } else {
+                mClearButton.setVisibility(View.VISIBLE);
+            }
+        } else {
+            if (mNotificationButtons.getVisibility() == View.VISIBLE) {
+                setViewVisibility(mClearButton, View.GONE, R.anim.notification_buttons_out);
+            } else {
+                mClearButton.setVisibility(View.GONE);
+            }
+        }
+
+        /*
+        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+        if (ongoing || latest) {
+            mNoNotificationsTitle.setVisibility(View.GONE);
+        } else {
+            mNoNotificationsTitle.setVisibility(View.VISIBLE);
+        }
+        */
+    }
+
     public void notificationIconsClicked(View v) {
         if (DEBUG) Slog.d(TAG, "clicked notification icons");
-        int msg = (mNotificationPanel.getVisibility() == View.GONE) 
-            ? MSG_OPEN_NOTIFICATION_PANEL
-            : MSG_CLOSE_NOTIFICATION_PANEL;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
+            int msg = (mNotificationPanel.getVisibility() == View.GONE) 
+                ? MSG_OPEN_NOTIFICATION_PANEL
+                : MSG_CLOSE_NOTIFICATION_PANEL;
+            mHandler.removeMessages(msg);
+            mHandler.sendEmptyMessage(msg);
+        }
     }
 
     public void systemInfoClicked(View v) {
         if (DEBUG) Slog.d(TAG, "clicked system info");
-        int msg = (mSystemPanel.getVisibility() == View.GONE) 
-            ? MSG_OPEN_SYSTEM_PANEL
-            : MSG_CLOSE_SYSTEM_PANEL;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
+            int msg = (mSystemPanel.getVisibility() == View.GONE) 
+                ? MSG_OPEN_SYSTEM_PANEL
+                : MSG_CLOSE_SYSTEM_PANEL;
+            mHandler.removeMessages(msg);
+            mHandler.sendEmptyMessage(msg);
+        }
     }
 
     public void recentButtonClicked(View v) {
@@ -690,10 +722,6 @@
         }
     }
 
-    Animation loadAnim(int id) {
-        return AnimationUtils.loadAnimation((Context)this, id);
-    }
-
     private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
         StatusBarNotification sbn = entry.notification;
         RemoteViews remoteViews = sbn.notification.contentView;
@@ -705,19 +733,23 @@
         LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
         View vetoButton = row.findViewById(R.id.veto);
-        final String _pkg = sbn.pkg;
-        final String _tag = sbn.tag;
-        final int _id = sbn.id;
-        vetoButton.setOnClickListener(new View.OnClickListener() { 
-                public void onClick(View v) {
-                    try {
-                        mBarService.onNotificationClear(_pkg, _tag, _id);
-                    } catch (RemoteException ex) {
-                        // system process is dead if we're here.
+        if (entry.notification.isClearable()) {
+            final String _pkg = sbn.pkg;
+            final String _tag = sbn.tag;
+            final int _id = sbn.id;
+            vetoButton.setOnClickListener(new View.OnClickListener() { 
+                    public void onClick(View v) {
+                        try {
+                            mBarService.onNotificationClear(_pkg, _tag, _id);
+                        } catch (RemoteException ex) {
+                            // system process is dead if we're here.
+                        }
+    //                    animateCollapse();
                     }
-//                    animateCollapse();
-                }
-            });
+                });
+        } else {
+            vetoButton.setVisibility(View.INVISIBLE);
+        }
 
         // bind the click event to the content area
         ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
@@ -802,6 +834,14 @@
             return false;
         }
     }
+
+    private void setViewVisibility(View v, int vis, int anim) {
+        if (v.getVisibility() != vis) {
+            //Slog.d(TAG, "setViewVisibility vis=" + (vis == View.VISIBLE) + " v=" + v);
+            v.setAnimation(AnimationUtils.loadAnimation((Context)this, anim));
+            v.setVisibility(vis);
+        }
+    }
 }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index 7ac7919..3c3139f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -59,7 +59,6 @@
 
         // TODO: Make this a configuration value.
         // 3 is enough to let us see most cases, but not get so far behind that it's annoying.
-        int mQueuePos = 0;
         mQueue = new StatusBarNotification[3];
     }
 
@@ -80,6 +79,18 @@
         }
     }
 
+    public void halt() {
+        removeMessages(MSG_ADVANCE);
+        if (mCurrentView != null) {
+            final int N = mQueue.length;
+            for (int i=0; i<N; i++) {
+                mQueue[i] = null;
+            }
+            mQueuePos = 0;
+            sendEmptyMessage(MSG_ADVANCE);
+        }
+    }
+
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case MSG_ADVANCE:
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8a732ed..97b8086 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -5332,6 +5332,15 @@
         }
     }
 
+    // Release effect engine here so that it is done immediately. Otherwise it will be released
+    // by the destructor when the last strong reference on the this object is released which can
+    // happen after next process is called on this effect.
+    if (size == 0 && mEffectInterface != NULL) {
+        // release effect engine
+        EffectRelease(mEffectInterface);
+        mEffectInterface = NULL;
+    }
+
     return size;
 }
 
@@ -6145,21 +6154,36 @@
 // Must be called with EffectChain::mLock locked
 void AudioFlinger::EffectChain::process_l()
 {
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread == 0) {
+        LOGW("process_l(): cannot promote mixer thread");
+        return;
+    }
+    PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+    bool isGlobalSession = (mSessionId == AudioSystem::SESSION_OUTPUT_MIX) ||
+            (mSessionId == AudioSystem::SESSION_OUTPUT_STAGE);
+    bool tracksOnSession = false;
+    if (!isGlobalSession) {
+        tracksOnSession =
+                playbackThread->hasAudioSession(mSessionId) & PlaybackThread::TRACK_SESSION;
+    }
+
     size_t size = mEffects.size();
-    for (size_t i = 0; i < size; i++) {
-        mEffects[i]->process();
+    // do not process effect if no track is present in same audio session
+    if (isGlobalSession || tracksOnSession) {
+        for (size_t i = 0; i < size; i++) {
+            mEffects[i]->process();
+        }
     }
     for (size_t i = 0; i < size; i++) {
         mEffects[i]->updateState();
     }
     // if no track is active, input buffer must be cleared here as the mixer process
     // will not do it
-    if (mSessionId > 0 && activeTracks() == 0) {
-        sp<ThreadBase> thread = mThread.promote();
-        if (thread != 0) {
-            size_t numSamples = thread->frameCount() * thread->channelCount();
-            memset(mInBuffer, 0, numSamples * sizeof(int16_t));
-        }
+    if (tracksOnSession &&
+        activeTracks() == 0) {
+        size_t numSamples = playbackThread->frameCount() * playbackThread->channelCount();
+        memset(mInBuffer, 0, numSamples * sizeof(int16_t));
     }
 }
 
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index f943a10..808c679 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -320,6 +320,7 @@
     // Callback is disabled by default
     mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
     mOrientation = 0;
+    mOrientationChanged = false;
     mPlayShutterSound = true;
     cameraService->setCameraBusy(cameraId);
     cameraService->loadSound();
@@ -491,6 +492,7 @@
             // Force the destruction of any previous overlay
             sp<Overlay> dummy;
             mHardware->setOverlay(dummy);
+            mOverlayRef = 0;
         }
     }
     if (surface != 0) {
@@ -518,11 +520,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;
@@ -802,6 +805,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;
@@ -813,20 +817,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;
     } else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) {
         switch (arg1) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 2b5c511..d57364a 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -173,7 +173,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;
         bool                            mPlayShutterSound;
 
         // Ensures atomicity among the public methods
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 29ca9a4..fe306b3 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -77,6 +77,8 @@
     private static native InputDevice nativeGetInputDevice(int deviceId);
     private static native void nativeGetInputConfiguration(Configuration configuration);
     private static native int[] nativeGetInputDeviceIds();
+    private static native boolean nativeTransferTouchFocus(InputChannel fromChannel,
+            InputChannel toChannel);
     private static native String nativeDump();
     
     // Input event injection constants defined in InputDispatcher.h.
@@ -320,6 +322,29 @@
         nativeSetInputDispatchMode(enabled, frozen);
     }
     
+    /**
+     * Atomically transfers touch focus from one window to another as identified by
+     * their input channels.  It is possible for multiple windows to have
+     * touch focus if they support split touch dispatch
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+     * method only transfers touch focus of the specified window without affecting
+     * other windows that may also have touch focus at the same time.
+     * @param fromChannel The channel of a window that currently has touch focus.
+     * @param toChannel The channel of the window that should receive touch focus in
+     * place of the first.
+     * @return True if the transfer was successful.  False if the window with the
+     * specified channel did not actually have touch focus at the time of the request.
+     */
+    public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
+        if (fromChannel == null) {
+            throw new IllegalArgumentException("fromChannel must not be null.");
+        }
+        if (toChannel == null) {
+            throw new IllegalArgumentException("toChannel must not be null.");
+        }
+        return nativeTransferTouchFocus(fromChannel, toChannel);
+    }
+    
     public void dump(PrintWriter pw) {
         String dumpStr = nativeDump();
         if (dumpStr != null) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 32f5e73..e6c6953 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -46,6 +46,7 @@
 import android.os.storage.StorageResultCode;
 import android.util.Slog;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -150,7 +151,7 @@
      * Mounted OBB tracking information. Used to track the current state of all
      * OBBs.
      */
-    final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
+    final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>();
     final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
 
     class ObbState implements IBinder.DeathRecipient {
@@ -162,13 +163,13 @@
         }
 
         // OBB source filename
-        String filename;
+        final String filename;
 
         // Token of remote Binder caller
-        IObbActionListener token;
+        final IObbActionListener token;
 
         // Binder.callingUid()
-        public int callerUid;
+        final public int callerUid;
 
         // Whether this is mounted currently.
         boolean mounted;
@@ -227,9 +228,9 @@
     private static final int MAX_UNMOUNT_RETRIES = 4;
 
     class UnmountCallBack {
-        String path;
+        final String path;
+        final boolean force;
         int retries;
-        boolean force;
 
         UnmountCallBack(String path, boolean force) {
             retries = 0;
@@ -244,7 +245,7 @@
     }
 
     class UmsEnableCallBack extends UnmountCallBack {
-        String method;
+        final String method;
 
         UmsEnableCallBack(String path, String method, boolean force) {
             super(path, force);
@@ -1526,10 +1527,6 @@
                 throw new IllegalArgumentException("OBB file is already mounted");
             }
 
-            if (mObbMounts.containsKey(token)) {
-                throw new IllegalArgumentException("You may only have one OBB mounted at a time");
-            }
-
             final int callerUid = Binder.getCallingUid();
             obbState = new ObbState(filename, token, callerUid);
             addObbState(obbState);
@@ -1567,14 +1564,25 @@
 
     private void addObbState(ObbState obbState) {
         synchronized (mObbMounts) {
-            mObbMounts.put(obbState.token, obbState);
+            List<ObbState> obbStates = mObbMounts.get(obbState.token);
+            if (obbStates == null) {
+                obbStates = new ArrayList<ObbState>();
+                mObbMounts.put(obbState.token, obbStates);
+            }
+            obbStates.add(obbState);
             mObbPathToStateMap.put(obbState.filename, obbState);
         }
     }
 
     private void removeObbState(ObbState obbState) {
         synchronized (mObbMounts) {
-            mObbMounts.remove(obbState.token);
+            final List<ObbState> obbStates = mObbMounts.get(obbState.token);
+            if (obbStates != null) {
+                obbStates.remove(obbState);
+            }
+            if (obbStates == null || obbStates.isEmpty()) {
+                mObbMounts.remove(obbState.token);
+            }
             mObbPathToStateMap.remove(obbState.filename);
         }
     }
@@ -1750,7 +1758,7 @@
             }
         }
 
-        abstract void handleExecute() throws RemoteException;
+        abstract void handleExecute() throws RemoteException, IOException;
         abstract void handleError();
     }
 
@@ -1762,8 +1770,12 @@
             mKey = key;
         }
 
-        public void handleExecute() throws RemoteException {
+        public void handleExecute() throws RemoteException, IOException {
             ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+            if (obbInfo == null) {
+                throw new IOException("Couldn't read OBB file");
+            }
+
             if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
                 throw new IllegalArgumentException("Caller package does not match OBB file");
             }
@@ -1786,15 +1798,17 @@
 
             if (rc == StorageResultCode.OperationSucceeded) {
                 try {
-                    mObbState.token.onObbResult(mObbState.filename, "mounted");
+                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                 }
             } else {
-                Slog.e(TAG, "Couldn't mount OBB file");
+                Slog.e(TAG, "Couldn't mount OBB file: " + rc);
 
                 // We didn't succeed, so remove this from the mount-set.
                 removeObbState(mObbState);
+
+                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
             }
         }
 
@@ -1802,7 +1816,7 @@
             removeObbState(mObbState);
 
             try {
-                mObbState.token.onObbResult(mObbState.filename, "error");
+                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
             }
@@ -1831,8 +1845,11 @@
             mForceUnmount = force;
         }
 
-        public void handleExecute() throws RemoteException {
+        public void handleExecute() throws RemoteException, IOException {
             ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+            if (obbInfo == null) {
+                throw new IOException("Couldn't read OBB file");
+            }
 
             if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
                 throw new IllegalArgumentException("Caller package does not match OBB file");
@@ -1856,13 +1873,13 @@
                 removeObbState(mObbState);
 
                 try {
-                    mObbState.token.onObbResult(mObbState.filename, "unmounted");
+                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                 }
             } else {
                 try {
-                    mObbState.token.onObbResult(mObbState.filename, "error");
+                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                 }
@@ -1873,7 +1890,7 @@
             removeObbState(mObbState);
 
             try {
-                mObbState.token.onObbResult(mObbState.filename, "error");
+                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
             }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index c82a085..8f90756 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;
@@ -8304,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();
@@ -8401,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/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5ca386b..717f63c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,9 +17,9 @@
 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;
+import com.trustedlogic.trustednfc.android.server.NfcService;
 
 import dalvik.system.VMRuntime;
 import dalvik.system.Zygote;
@@ -45,6 +45,7 @@
 import android.server.BluetoothService;
 import android.server.search.SearchManagerService;
 import android.util.EventLog;
+import android.util.Log;
 import android.util.Slog;
 
 import java.io.File;
@@ -414,6 +415,20 @@
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting Recognition Service", e);
             }
+            
+            try {
+                Slog.i(TAG, "Nfc Service");
+                NfcService nfc;
+                try {
+                    nfc = new NfcService(context);
+                } catch (UnsatisfiedLinkError e) { // gross hack to detect NFC
+                    nfc = null;
+                    Slog.w(TAG, "No NFC support");
+                }
+                ServiceManager.addService(Context.NFC_SERVICE, nfc);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting NFC Service", e);
+            }
 
             try {
                 Slog.i(TAG, "DiskStats Service");
@@ -435,16 +450,6 @@
             }
 
             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);
-            }
-
-            try {
                 Slog.i(TAG, "NetworkTimeUpdateService");
                 networkTimeUpdater = new NetworkTimeUpdateService(context);
             } catch (Throwable e) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 7b2a570..19f56a8 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -21,7 +21,9 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -86,6 +88,7 @@
 
     private AlarmManager mAlarmManager;
     private PendingIntent mIdleIntent;
+    private BluetoothA2dp mBluetoothA2dp;
     private static final int IDLE_REQUEST = 0;
     private boolean mScreenOff;
     private boolean mDeviceIdle;
@@ -182,7 +185,7 @@
      * something other than scanning, we reset this to 0.
      */
     private int mNumScansSinceNetworkStateChange;
-    
+
     /**
      * Temporary for computing UIDS that are responsible for starting WIFI.
      * Protected by mWifiStateTracker lock.
@@ -888,17 +891,10 @@
                     return;
                 }
                 mPluggedType = pluggedType;
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-                Set<BluetoothDevice> sinks = a2dp.getConnectedSinks();
-                boolean isBluetoothPlaying = false;
-                for (BluetoothDevice sink : sinks) {
-                    if (a2dp.getSinkState(sink) == BluetoothA2dp.STATE_PLAYING) {
-                        isBluetoothPlaying = true;
-                    }
-                }
-                mWifiStateMachine.setBluetoothScanMode(isBluetoothPlaying);
-
+            } else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) {
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                                               BluetoothA2dp.STATE_NOT_PLAYING);
+                mWifiStateMachine.setBluetoothScanMode(state == BluetoothA2dp.STATE_PLAYING);
             } else {
                 return;
             }
@@ -958,7 +954,7 @@
         }
         mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
     }
-    
+
     private void updateWifiState() {
         boolean wifiEnabled = getPersistedWifiEnabled();
         boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get();
@@ -999,7 +995,7 @@
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         intentFilter.addAction(ACTION_DEVICE_IDLE);
-        intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+        intentFilter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
         mContext.registerReceiver(mReceiver, intentFilter);
     }
 
@@ -1191,7 +1187,7 @@
     }
 
     private boolean acquireWifiLockLocked(WifiLock wifiLock) {
-        Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
+        if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
 
         mLocks.addLock(wifiLock);
 
@@ -1214,7 +1210,7 @@
         // Be aggressive about adding new locks into the accounted state...
         // we want to over-report rather than under-report.
         reportStartWorkSource();
-        
+
         updateWifiState();
         return true;
     }
@@ -1258,7 +1254,7 @@
 
         WifiLock wifiLock = mLocks.removeLock(lock);
 
-        Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
+        if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
 
         hadLock = (wifiLock != null);
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 9685fb7..5c4b919 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4225,29 +4225,75 @@
     }
 
     private final boolean checkHoldingPermissionsLocked(IPackageManager pm,
-            ProviderInfo pi, int uid, int modeFlags) {
+            ProviderInfo pi, Uri uri, int uid, int modeFlags) {
+        boolean readPerm = (modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
+        boolean writePerm = (modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid);
         try {
-            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-                if ((pi.readPermission != null) &&
+            // Is the component private from the target uid?
+            final boolean prv = !pi.exported && pi.applicationInfo.uid != uid;
+
+            // Acceptable if the there is no read permission needed from the
+            // target or the target is holding the read permission.
+            if (!readPerm) {
+                if ((!prv && pi.readPermission == null) ||
                         (pm.checkUidPermission(pi.readPermission, uid)
-                                != PackageManager.PERMISSION_GRANTED)) {
-                    return false;
+                                == PackageManager.PERMISSION_GRANTED)) {
+                    readPerm = true;
                 }
             }
-            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-                if ((pi.writePermission != null) &&
+
+            // Acceptable if the there is no write permission needed from the
+            // target or the target is holding the read permission.
+            if (!writePerm) {
+                if (!prv && (pi.writePermission == null) ||
                         (pm.checkUidPermission(pi.writePermission, uid)
-                                != PackageManager.PERMISSION_GRANTED)) {
-                    return false;
+                                == PackageManager.PERMISSION_GRANTED)) {
+                    writePerm = true;
                 }
             }
-            if (!pi.exported && pi.applicationInfo.uid != uid) {
-                return false;
+
+            // Acceptable if there is a path permission matching the URI that
+            // the target holds the permission on.
+            PathPermission[] pps = pi.pathPermissions;
+            if (pps != null && (!readPerm || !writePerm)) {
+                final String path = uri.getPath();
+                int i = pps.length;
+                while (i > 0 && (!readPerm || !writePerm)) {
+                    i--;
+                    PathPermission pp = pps[i];
+                    if (!readPerm) {
+                        final String pprperm = pp.getReadPermission();
+                        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking read perm for "
+                                + pprperm + " for " + pp.getPath()
+                                + ": match=" + pp.match(path)
+                                + " check=" + pm.checkUidPermission(pprperm, uid));
+                        if (pprperm != null && pp.match(path) &&
+                                (pm.checkUidPermission(pprperm, uid)
+                                        == PackageManager.PERMISSION_GRANTED)) {
+                            readPerm = true;
+                        }
+                    }
+                    if (!writePerm) {
+                        final String ppwperm = pp.getWritePermission();
+                        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking write perm "
+                                + ppwperm + " for " + pp.getPath()
+                                + ": match=" + pp.match(path)
+                                + " check=" + pm.checkUidPermission(ppwperm, uid));
+                        if (ppwperm != null && pp.match(path) &&
+                                (pm.checkUidPermission(ppwperm, uid)
+                                        == PackageManager.PERMISSION_GRANTED)) {
+                            writePerm = true;
+                        }
+                    }
+                }
             }
-            return true;
         } catch (RemoteException e) {
             return false;
         }
+
+        return readPerm && writePerm;
     }
 
     private final boolean checkUriPermissionLocked(Uri uri, int uid,
@@ -4340,7 +4386,7 @@
         }
 
         // First...  does the target actually need this permission?
-        if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) {
+        if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
             // No need to grant the target this permission.
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Target " + targetPkg + " already has full permission to " + uri);
@@ -4374,7 +4420,7 @@
 
         // Third...  does the caller itself have permission to access
         // this uri?
-        if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
+        if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
             if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
                 throw new SecurityException("Uid " + callingUid
                         + " does not have permission to uri " + uri);
@@ -4542,7 +4588,7 @@
         }
 
         // Does the caller have this permission on the URI?
-        if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
+        if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
             // Right now, if you are not the original owner of the permission,
             // you are not allowed to revoke it.
             //if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
@@ -5599,6 +5645,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
     // =========================================================
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 73a5435..0a98ebd 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -16,7 +16,9 @@
 
 package com.android.server.am;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
@@ -43,6 +45,8 @@
     
     final BatteryStatsImpl mStats;
     Context mContext;
+    private boolean mBluetoothPendingStats;
+    private BluetoothHeadset mBluetoothHeadset;
 
     BatteryStatsService(String filename) {
         mStats = new BatteryStatsImpl(filename);
@@ -283,16 +287,43 @@
 
     public void noteBluetoothOn() {
         enforceCallingPermission();
-        BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
         synchronized (mStats) {
-            mStats.noteBluetoothOnLocked();
-            mStats.setBtHeadset(headset);
+            if (mBluetoothHeadset != null) {
+                mStats.noteBluetoothOnLocked();
+                mStats.setBtHeadset(mBluetoothHeadset);
+            } else {
+                mBluetoothPendingStats = true;
+            }
         }
     }
-    
+
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mBluetoothHeadset = (BluetoothHeadset) proxy;
+            synchronized (mStats) {
+                if (mBluetoothPendingStats) {
+                    mStats.noteBluetoothOnLocked();
+                    mStats.setBtHeadset(mBluetoothHeadset);
+                    mBluetoothPendingStats = false;
+                }
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            mBluetoothHeadset = null;
+        }
+    };
+
     public void noteBluetoothOff() {
         enforceCallingPermission();
         synchronized (mStats) {
+            mBluetoothPendingStats = false;
             mStats.noteBluetoothOffLocked();
         }
     }
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/trustedlogic/trustednfc/android/server/NfcService.java b/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java
new file mode 100644
index 0000000..431b798
--- /dev/null
+++ b/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java
@@ -0,0 +1,2111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trustedlogic.trustednfc.android.server;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Set;
+
+import com.trustedlogic.trustednfc.android.ILlcpConnectionlessSocket;
+import com.trustedlogic.trustednfc.android.ILlcpServiceSocket;
+import com.trustedlogic.trustednfc.android.INfcManager;
+import com.trustedlogic.trustednfc.android.ILlcpSocket;
+import com.trustedlogic.trustednfc.android.INfcTag;
+import com.trustedlogic.trustednfc.android.IP2pInitiator;
+import com.trustedlogic.trustednfc.android.IP2pTarget;
+import com.trustedlogic.trustednfc.android.LlcpPacket;
+import com.trustedlogic.trustednfc.android.NdefMessage;
+import com.trustedlogic.trustednfc.android.NfcException;
+import com.trustedlogic.trustednfc.android.NfcManager;
+import com.trustedlogic.trustednfc.android.internal.NativeLlcpConnectionlessSocket;
+import com.trustedlogic.trustednfc.android.internal.NativeLlcpServiceSocket;
+import com.trustedlogic.trustednfc.android.internal.NativeLlcpSocket;
+import com.trustedlogic.trustednfc.android.internal.NativeNfcManager;
+import com.trustedlogic.trustednfc.android.internal.NativeNfcTag;
+import com.trustedlogic.trustednfc.android.internal.NativeP2pDevice;
+import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+public class NfcService extends INfcManager.Stub implements Runnable {
+
+    /**
+     * NFC Service tag
+     */
+    private static final String TAG = "NfcService";
+
+    /**
+     * NFC features disabled state
+     */
+    private static final short NFC_STATE_DISABLED = 0x00;
+
+    /**
+     * NFC features enabled state
+     */
+    private static final short NFC_STATE_ENABLED = 0x01;
+
+    /**
+     * NFC Discovery for Reader mode
+     */
+    private static final int DISCOVERY_MODE_READER = 0;
+
+    /**
+     * NFC Discovery for Card Emulation Mode
+     */
+    private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
+
+    /**
+     * LLCP Service Socket type
+     */
+    private static final int LLCP_SERVICE_SOCKET_TYPE = 0;
+
+    /**
+     * LLCP Socket type
+     */
+    private static final int LLCP_SOCKET_TYPE = 1;
+
+    /**
+     * LLCP Connectionless socket type
+     */
+    private static final int LLCP_CONNECTIONLESS_SOCKET_TYPE = 2;
+
+    /**
+     * Maximun number of sockets managed
+     */
+    private static final int LLCP_SOCKET_NB_MAX = 5;
+
+    /**
+     * Default value for the Maximum Information Unit parameter
+     */
+    private static final int LLCP_LTO_DEFAULT_VALUE = 150;
+
+    /**
+     * Default value for the Maximum Information Unit parameter
+     */
+    private static final int LLCP_LTO_MAX_VALUE = 255;
+
+    /**
+     * Maximun value for the Receive Window
+     */
+    private static final int LLCP_RW_MAX_VALUE = 15;
+
+    /**
+     * Default value for the Maximum Information Unit parameter
+     */
+    private static final int LLCP_MIU_DEFAULT_VALUE = 128;
+
+    /**
+     * Default value for the Maximum Information Unit parameter
+     */
+    private static final int LLCP_MIU_MAX_VALUE = 2176;
+
+    /**
+     * Default value for the Well Known Service List parameter
+     */
+    private static final int LLCP_WKS_DEFAULT_VALUE = 1;
+
+    /**
+     * Max value for the Well Known Service List parameter
+     */
+    private static final int LLCP_WKS_MAX_VALUE = 15;
+
+    /**
+     * Default value for the Option parameter
+     */
+    private static final int LLCP_OPT_DEFAULT_VALUE = 0;
+
+    /**
+     * Max value for the Option parameter
+     */
+    private static final int LLCP_OPT_MAX_VALUE = 3;
+
+    /**
+     * LLCP Properties
+     */
+    private static final int PROPERTY_LLCP_LTO = 0;
+
+    private static final int PROPERTY_LLCP_MIU = 1;
+
+    private static final int PROPERTY_LLCP_WKS = 2;
+
+    private static final int PROPERTY_LLCP_OPT = 3;
+
+    private static final String PROPERTY_LLCP_LTO_VALUE = "llcp.lto";
+
+    private static final String PROPERTY_LLCP_MIU_VALUE = "llcp.miu";
+
+    private static final String PROPERTY_LLCP_WKS_VALUE = "llcp.wks";
+
+    private static final String PROPERTY_LLCP_OPT_VALUE = "llcp.opt";
+
+    /**
+     * NFC Reader Properties
+     */
+    private static final int PROPERTY_NFC_DISCOVERY_A = 4;
+
+    private static final int PROPERTY_NFC_DISCOVERY_B = 5;
+
+    private static final int PROPERTY_NFC_DISCOVERY_F = 6;
+
+    private static final int PROPERTY_NFC_DISCOVERY_15693 = 7;
+
+    private static final int PROPERTY_NFC_DISCOVERY_NFCIP = 8;
+
+    private static final String PROPERTY_NFC_DISCOVERY_A_VALUE = "discovery.iso14443A";
+
+    private static final String PROPERTY_NFC_DISCOVERY_B_VALUE = "discovery.iso14443B";
+
+    private static final String PROPERTY_NFC_DISCOVERY_F_VALUE = "discovery.felica";
+
+    private static final String PROPERTY_NFC_DISCOVERY_15693_VALUE = "discovery.iso15693";
+
+    private static final String PROPERTY_NFC_DISCOVERY_NFCIP_VALUE = "discovery.nfcip";
+
+    private Context mContext;
+
+    private HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
+
+    private HashMap<Integer, Object> mSocketMap = new HashMap<Integer, Object>();
+
+    private LinkedList<RegisteredSocket> mRegisteredSocketList = new LinkedList<RegisteredSocket>();
+
+    private int mLlcpLinkState = NfcManager.LLCP_LINK_STATE_DEACTIVATED;
+
+    private int mGeneratedSocketHandle = 0;
+
+    private int mNbSocketCreated = 0;
+
+    private boolean mIsNfcEnabled = false;
+
+    private NfcHandler mNfcHandler;
+
+    private int mSelectedSeId = 0;
+
+    private int mTimeout = 0;
+
+    private int mNfcState;
+
+    private int mNfcSecureElementState;
+
+    private boolean mOpenPending = false;
+
+    private NativeNfcManager mManager;
+
+    private ILlcpSocket mLlcpSocket = new ILlcpSocket.Stub() {
+
+        public int close(int nativeHandle) throws RemoteException {
+            NativeLlcpSocket socket = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+                    isSuccess = socket.doClose();
+                    if (isSuccess) {
+                        /* Remove the socket closed from the hmap */
+                        RemoveSocket(nativeHandle);
+                        /* Update mNbSocketCreated */
+                        mNbSocketCreated--;
+                        return ErrorCodes.SUCCESS;
+                    } else {
+                        return ErrorCodes.ERROR_IO;
+                    }
+                } else {
+                    /* Remove the socket closed from the hmap */
+                    RemoveSocket(nativeHandle);
+
+                    /* Remove registered socket from the list */
+                    RemoveRegisteredSocket(nativeHandle);
+
+                    /* Update mNbSocketCreated */
+                    mNbSocketCreated--;
+
+                    return ErrorCodes.SUCCESS;
+                }
+            } else {
+                return ErrorCodes.ERROR_IO;
+            }
+        }
+
+        public int connect(int nativeHandle, int sap) throws RemoteException {
+            NativeLlcpSocket socket = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                isSuccess = socket.doConnect(sap, socket.getConnectTimeout());
+                if (isSuccess) {
+                    return ErrorCodes.SUCCESS;
+                } else {
+                    return ErrorCodes.ERROR_IO;
+                }
+            } else {
+                return ErrorCodes.ERROR_IO;
+            }
+
+        }
+
+        public int connectByName(int nativeHandle, String sn) throws RemoteException {
+            NativeLlcpSocket socket = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                isSuccess = socket.doConnectBy(sn, socket.getConnectTimeout());
+                if (isSuccess) {
+                    return ErrorCodes.SUCCESS;
+                } else {
+                    return ErrorCodes.ERROR_IO;
+                }
+            } else {
+                return ErrorCodes.ERROR_IO;
+            }
+
+        }
+
+        public int getConnectTimeout(int nativeHandle) throws RemoteException {
+            NativeLlcpSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                return socket.getConnectTimeout();
+            } else {
+                return 0;
+            }
+        }
+
+        public int getLocalSap(int nativeHandle) throws RemoteException {
+            NativeLlcpSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                return socket.getSap();
+            } else {
+                return 0;
+            }
+        }
+
+        public int getLocalSocketMiu(int nativeHandle) throws RemoteException {
+            NativeLlcpSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                return socket.getMiu();
+            } else {
+                return 0;
+            }
+        }
+
+        public int getLocalSocketRw(int nativeHandle) throws RemoteException {
+            NativeLlcpSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                return socket.getRw();
+            } else {
+                return 0;
+            }
+        }
+
+        public int getRemoteSocketMiu(int nativeHandle) throws RemoteException {
+            NativeLlcpSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                if (socket.doGetRemoteSocketMiu() != 0) {
+                    return socket.doGetRemoteSocketMiu();
+                } else {
+                    return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED;
+                }
+            } else {
+                return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED;
+            }
+        }
+
+        public int getRemoteSocketRw(int nativeHandle) throws RemoteException {
+            NativeLlcpSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                if (socket.doGetRemoteSocketRw() != 0) {
+                    return socket.doGetRemoteSocketRw();
+                } else {
+                    return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED;
+                }
+            } else {
+                return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED;
+            }
+        }
+
+        public int receive(int nativeHandle, byte[] receiveBuffer) throws RemoteException {
+            NativeLlcpSocket socket = null;
+            int receiveLength = 0;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                receiveLength = socket.doReceive(receiveBuffer);
+                if (receiveLength != 0) {
+                    return receiveLength;
+                } else {
+                    return ErrorCodes.ERROR_IO;
+                }
+            } else {
+                return ErrorCodes.ERROR_IO;
+            }
+        }
+
+        public int send(int nativeHandle, byte[] data) throws RemoteException {
+            NativeLlcpSocket socket = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                isSuccess = socket.doSend(data);
+                if (isSuccess) {
+                    return ErrorCodes.SUCCESS;
+                } else {
+                    return ErrorCodes.ERROR_IO;
+                }
+            } else {
+                return ErrorCodes.ERROR_IO;
+            }
+        }
+
+        public void setConnectTimeout(int nativeHandle, int timeout) throws RemoteException {
+            NativeLlcpSocket socket = null;
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                socket.setConnectTimeout(timeout);
+            }
+        }
+
+    };
+
+    private ILlcpServiceSocket mLlcpServerSocketService = new ILlcpServiceSocket.Stub() {
+
+        public int accept(int nativeHandle) throws RemoteException {
+            NativeLlcpServiceSocket socket = null;
+            NativeLlcpSocket clientSocket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) {
+                /* find the socket in the hmap */
+                socket = (NativeLlcpServiceSocket) findSocket(nativeHandle);
+                if (socket != null) {
+                    clientSocket = socket.doAccept(socket.getAcceptTimeout(), socket.getMiu(),
+                            socket.getRw(), socket.getLinearBufferLength());
+                    if (clientSocket != null) {
+                        /* Add the socket into the socket map */
+                        mSocketMap.put(clientSocket.getHandle(), clientSocket);
+                        mNbSocketCreated++;
+                        return clientSocket.getHandle();
+                    } else {
+                        return ErrorCodes.ERROR_IO;
+                    }
+                } else {
+                    return ErrorCodes.ERROR_IO;
+                }
+            } else {
+                return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES;
+            }
+
+        }
+
+        public void close(int nativeHandle) throws RemoteException {
+            NativeLlcpServiceSocket socket = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpServiceSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+                    isSuccess = socket.doClose();
+                    if (isSuccess) {
+                        /* Remove the socket closed from the hmap */
+                        RemoveSocket(nativeHandle);
+                        /* Update mNbSocketCreated */
+                        mNbSocketCreated--;
+                    }
+                } else {
+                    /* Remove the socket closed from the hmap */
+                    RemoveSocket(nativeHandle);
+
+                    /* Remove registered socket from the list */
+                    RemoveRegisteredSocket(nativeHandle);
+
+                    /* Update mNbSocketCreated */
+                    mNbSocketCreated--;
+                }
+            }
+        }
+
+        public int getAcceptTimeout(int nativeHandle) throws RemoteException {
+            NativeLlcpServiceSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpServiceSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                return socket.getAcceptTimeout();
+            } else {
+                return 0;
+            }
+        }
+
+        public void setAcceptTimeout(int nativeHandle, int timeout) throws RemoteException {
+            NativeLlcpServiceSocket socket = null;
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpServiceSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                socket.setAcceptTimeout(timeout);
+            }
+        }
+    };
+
+    private ILlcpConnectionlessSocket mLlcpConnectionlessSocketService = new ILlcpConnectionlessSocket.Stub() {
+
+        public void close(int nativeHandle) throws RemoteException {
+            NativeLlcpConnectionlessSocket socket = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+                    isSuccess = socket.doClose();
+                    if (isSuccess) {
+                        /* Remove the socket closed from the hmap */
+                        RemoveSocket(nativeHandle);
+                        /* Update mNbSocketCreated */
+                        mNbSocketCreated--;
+                    }
+                } else {
+                    /* Remove the socket closed from the hmap */
+                    RemoveSocket(nativeHandle);
+
+                    /* Remove registered socket from the list */
+                    RemoveRegisteredSocket(nativeHandle);
+
+                    /* Update mNbSocketCreated */
+                    mNbSocketCreated--;
+                }
+            }
+        }
+
+        public int getSap(int nativeHandle) throws RemoteException {
+            NativeLlcpConnectionlessSocket socket = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                return socket.getSap();
+            } else {
+                return 0;
+            }
+        }
+
+        public LlcpPacket receiveFrom(int nativeHandle) throws RemoteException {
+            NativeLlcpConnectionlessSocket socket = null;
+            LlcpPacket packet;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                packet = socket.doReceiveFrom(socket.getLinkMiu());
+                if (packet != null) {
+                    return packet;
+                }
+                return null;
+            } else {
+                return null;
+            }
+        }
+
+        public int sendTo(int nativeHandle, LlcpPacket packet) throws RemoteException {
+            NativeLlcpConnectionlessSocket socket = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the socket in the hmap */
+            socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle);
+            if (socket != null) {
+                isSuccess = socket.doSendTo(packet.getRemoteSap(), packet.getDataBuffer());
+                if (isSuccess) {
+                    return ErrorCodes.SUCCESS;
+                } else {
+                    return ErrorCodes.ERROR_IO;
+                }
+            } else {
+                return ErrorCodes.ERROR_IO;
+            }
+        }
+    };
+
+    private INfcTag mNfcTagService = new INfcTag.Stub() {
+
+        public int close(int nativeHandle) throws RemoteException {
+            NativeNfcTag tag = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                if (tag.doDisconnect()) {
+                    /* Remove the device from the hmap */
+                    RemoveObject(nativeHandle);
+                    /* Restart polling loop for notification */
+                    mManager.enableDiscovery(DISCOVERY_MODE_READER);
+                    mOpenPending = false;
+                    return ErrorCodes.SUCCESS;
+                }
+
+            }
+            /* Restart polling loop for notification */
+            mManager.enableDiscovery(DISCOVERY_MODE_READER);
+            mOpenPending = false;
+            return ErrorCodes.ERROR_DISCONNECT;
+        }
+
+        public int connect(int nativeHandle) throws RemoteException {
+            NativeNfcTag tag = null;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                if (tag.doConnect())
+                    return ErrorCodes.SUCCESS;
+            }
+            /* Restart polling loop for notification */
+            mManager.enableDiscovery(DISCOVERY_MODE_READER);
+            mOpenPending = false;
+            return ErrorCodes.ERROR_CONNECT;
+        }
+
+        public String getType(int nativeHandle) throws RemoteException {
+            NativeNfcTag tag = null;
+            String type;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                type = tag.getType();
+                return type;
+            }
+            return null;
+        }
+
+        public byte[] getUid(int nativeHandle) throws RemoteException {
+            NativeNfcTag tag = null;
+            byte[] uid;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                uid = tag.getUid();
+                return uid;
+            }
+            return null;
+        }
+
+        public boolean isNdef(int nativeHandle) throws RemoteException {
+            NativeNfcTag tag = null;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return isSuccess;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                isSuccess = tag.checkNDEF();
+            }
+            return isSuccess;
+        }
+
+        public byte[] transceive(int nativeHandle, byte[] data) throws RemoteException {
+            NativeNfcTag tag = null;
+            byte[] response;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                response = tag.doTransceive(data);
+                return response;
+            }
+            return null;
+        }
+
+        public NdefMessage read(int nativeHandle) throws RemoteException {
+            NativeNfcTag tag;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                byte[] buf = tag.doRead();
+                if (buf == null)
+                    return null;
+
+                /* Create an NdefMessage */
+                try {
+                    return new NdefMessage(buf);
+                } catch (NfcException e) {
+                    return null;
+                }
+            }
+            return null;
+        }
+
+        public boolean write(int nativeHandle, NdefMessage msg) throws RemoteException {
+            NativeNfcTag tag;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return isSuccess;
+            }
+
+            /* find the tag in the hmap */
+            tag = (NativeNfcTag) findObject(nativeHandle);
+            if (tag != null) {
+                isSuccess = tag.doWrite(msg.toByteArray());
+            }
+            return isSuccess;
+
+        }
+
+    };
+
+    private IP2pInitiator mP2pInitiatorService = new IP2pInitiator.Stub() {
+
+        public byte[] getGeneralBytes(int nativeHandle) throws RemoteException {
+            NativeP2pDevice device;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                byte[] buff = device.getGeneralBytes();
+                if (buff == null)
+                    return null;
+                return buff;
+            }
+            return null;
+        }
+
+        public int getMode(int nativeHandle) throws RemoteException {
+            NativeP2pDevice device;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                return device.getMode();
+            }
+            return ErrorCodes.ERROR_INVALID_PARAM;
+        }
+
+        public byte[] receive(int nativeHandle) throws RemoteException {
+            NativeP2pDevice device;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                byte[] buff = device.doReceive();
+                if (buff == null)
+                    return null;
+                return buff;
+            }
+            /* Restart polling loop for notification */
+            mManager.enableDiscovery(DISCOVERY_MODE_READER);
+            mOpenPending = false;
+            return null;
+        }
+
+        public boolean send(int nativeHandle, byte[] data) throws RemoteException {
+            NativeP2pDevice device;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return isSuccess;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                isSuccess = device.doSend(data);
+            }
+            return isSuccess;
+        }
+    };
+
+    private IP2pTarget mP2pTargetService = new IP2pTarget.Stub() {
+
+        public int connect(int nativeHandle) throws RemoteException {
+            NativeP2pDevice device;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                if (device.doConnect()) {
+                    return ErrorCodes.SUCCESS;
+                }
+            }
+            return ErrorCodes.ERROR_CONNECT;
+        }
+
+        public boolean disconnect(int nativeHandle) throws RemoteException {
+            NativeP2pDevice device;
+            boolean isSuccess = false;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return isSuccess;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                if (isSuccess = device.doDisconnect()) {
+                    mOpenPending = false;
+                    /* remove the device from the hmap */
+                    RemoveObject(nativeHandle);
+                    /* Restart polling loop for notification */
+                    mManager.enableDiscovery(DISCOVERY_MODE_READER);
+                }
+            }
+            return isSuccess;
+
+        }
+
+        public byte[] getGeneralBytes(int nativeHandle) throws RemoteException {
+            NativeP2pDevice device;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                byte[] buff = device.getGeneralBytes();
+                if (buff == null)
+                    return null;
+                return buff;
+            }
+            return null;
+        }
+
+        public int getMode(int nativeHandle) throws RemoteException {
+            NativeP2pDevice device;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return ErrorCodes.ERROR_NOT_INITIALIZED;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                return device.getMode();
+            }
+            return ErrorCodes.ERROR_INVALID_PARAM;
+        }
+
+        public byte[] transceive(int nativeHandle, byte[] data) throws RemoteException {
+            NativeP2pDevice device;
+
+            // Check if NFC is enabled
+            if (!mIsNfcEnabled) {
+                return null;
+            }
+
+            /* find the device in the hmap */
+            device = (NativeP2pDevice) findObject(nativeHandle);
+            if (device != null) {
+                byte[] buff = device.doTransceive(data);
+                if (buff == null)
+                    return null;
+                return buff;
+            }
+            return null;
+        }
+    };
+
+    private class NfcHandler extends Handler {
+
+        @Override
+        public void handleMessage(Message msg) {
+            try {
+
+            } catch (Exception e) {
+                // Log, don't crash!
+                Log.e(TAG, "Exception in NfcHandler.handleMessage:", e);
+            }
+        }
+
+    };
+
+    public NfcService(Context context) {
+        super();
+        mContext = context;
+        mManager = new NativeNfcManager(mContext);
+
+        mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter(
+                NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION));
+
+        mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter(
+                NfcManager.LLCP_LINK_STATE_CHANGED_ACTION));
+        
+        mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter(
+                NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION));
+
+        Thread thread = new Thread(null, this, "NfcService");
+        thread.start();
+
+        mManager.initializeNativeStructure();
+
+            int nfcState = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NFC_ON, 0);
+
+            if (nfcState == NFC_STATE_ENABLED) {
+                if (this._enable()) {
+                }
+            }
+
+    }
+
+    public void run() {
+        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        Looper.prepare();
+        mNfcHandler = new NfcHandler();
+        Looper.loop();
+    }
+
+    public void cancel() throws RemoteException {
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW,
+                "NFC_RAW permission required to cancel NFC opening");
+        if (mOpenPending) {
+            mOpenPending = false;
+            mManager.doCancel();
+            /* Restart polling loop for notification */
+            mManager.enableDiscovery(DISCOVERY_MODE_READER);
+        }
+    }
+
+    public int createLlcpConnectionlessSocket(int sap) throws RemoteException {
+
+        // Check if NFC is enabled
+        if (!mIsNfcEnabled) {
+            return ErrorCodes.ERROR_NOT_INITIALIZED;
+        }
+
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_LLCP,
+                "NFC_LLCP permission required for LLCP operations with NFC service");
+
+        /* Check SAP is not already used */
+
+        /* Check nb socket created */
+        if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) {
+            /* Store the socket handle */
+            int sockeHandle = mGeneratedSocketHandle;
+
+            if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+                NativeLlcpConnectionlessSocket socket;
+
+                socket = mManager.doCreateLlcpConnectionlessSocket(sap);
+                if (socket != null) {
+                    /* Update the number of socket created */
+                    mNbSocketCreated++;
+
+                    /* Add the socket into the socket map */
+                    mSocketMap.put(sockeHandle, socket);
+
+                    return sockeHandle;
+                } else {
+                    /*
+                     * socket creation error - update the socket handle
+                     * generation
+                     */
+                    mGeneratedSocketHandle -= 1;
+
+                    /* Get Error Status */
+                    int errorStatus = mManager.doGetLastError();
+
+                    switch (errorStatus) {
+                        case ErrorCodes.ERROR_BUFFER_TO_SMALL:
+                            return ErrorCodes.ERROR_BUFFER_TO_SMALL;
+                        case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+                            return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES;
+                        default:
+                            return ErrorCodes.ERROR_SOCKET_CREATION;
+                    }
+                }
+            } else {
+                /* Check SAP is not already used */
+                if (!CheckSocketSap(sap)) {
+                    return ErrorCodes.ERROR_SAP_USED;
+                }
+
+                NativeLlcpConnectionlessSocket socket = new NativeLlcpConnectionlessSocket(sap);
+
+                /* Add the socket into the socket map */
+                mSocketMap.put(sockeHandle, socket);
+
+                /* Update the number of socket created */
+                mNbSocketCreated++;
+
+                /* Create new registered socket */
+                RegisteredSocket registeredSocket = new RegisteredSocket(
+                        LLCP_CONNECTIONLESS_SOCKET_TYPE, sockeHandle, sap);
+
+                /* Put this socket into a list of registered socket */
+                mRegisteredSocketList.add(registeredSocket);
+            }
+
+            /* update socket handle generation */
+            mGeneratedSocketHandle++;
+
+            return sockeHandle;
+
+        } else {
+            /* No socket available */
+            return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES;
+        }
+
+    }
+
+    public int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength)
+            throws RemoteException {
+
+        // Check if NFC is enabled
+        if (!mIsNfcEnabled) {
+            return ErrorCodes.ERROR_NOT_INITIALIZED;
+        }
+
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_LLCP,
+                "NFC_LLCP permission required for LLCP operations with NFC service");
+
+        if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) {
+            int sockeHandle = mGeneratedSocketHandle;
+
+            if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+                NativeLlcpServiceSocket socket;
+
+                socket = mManager.doCreateLlcpServiceSocket(sap, sn, miu, rw, linearBufferLength);
+                if (socket != null) {
+                    /* Update the number of socket created */
+                    mNbSocketCreated++;
+                    /* Add the socket into the socket map */
+                    mSocketMap.put(sockeHandle, socket);
+                } else {
+                    /* socket creation error - update the socket handle counter */
+                    mGeneratedSocketHandle -= 1;
+
+                    /* Get Error Status */
+                    int errorStatus = mManager.doGetLastError();
+
+                    switch (errorStatus) {
+                        case ErrorCodes.ERROR_BUFFER_TO_SMALL:
+                            return ErrorCodes.ERROR_BUFFER_TO_SMALL;
+                        case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+                            return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES;
+                        default:
+                            return ErrorCodes.ERROR_SOCKET_CREATION;
+                    }
+                }
+            } else {
+
+                /* Check SAP is not already used */
+                if (!CheckSocketSap(sap)) {
+                    return ErrorCodes.ERROR_SAP_USED;
+                }
+
+                /* Service Name */
+                if (!CheckSocketServiceName(sn)) {
+                    return ErrorCodes.ERROR_SERVICE_NAME_USED;
+                }
+
+                /* Check socket options */
+                if (!CheckSocketOptions(miu, rw, linearBufferLength)) {
+                    return ErrorCodes.ERROR_SOCKET_OPTIONS;
+                }
+
+                NativeLlcpServiceSocket socket = new NativeLlcpServiceSocket(sn);
+
+                /* Add the socket into the socket map */
+                mSocketMap.put(sockeHandle, socket);
+
+                /* Update the number of socket created */
+                mNbSocketCreated++;
+
+                /* Create new registered socket */
+                RegisteredSocket registeredSocket = new RegisteredSocket(LLCP_SERVICE_SOCKET_TYPE,
+                        sockeHandle, sap, sn, miu, rw, linearBufferLength);
+
+                /* Put this socket into a list of registered socket */
+                mRegisteredSocketList.add(registeredSocket);
+            }
+
+            /* update socket handle generation */
+            mGeneratedSocketHandle += 1;
+
+            Log.d(TAG, "Llcp Service Socket Handle =" + sockeHandle);
+            return sockeHandle;
+        } else {
+            /* No socket available */
+            return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    public int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength)
+            throws RemoteException {
+
+        // Check if NFC is enabled
+        if (!mIsNfcEnabled) {
+            return ErrorCodes.ERROR_NOT_INITIALIZED;
+        }
+
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_LLCP,
+                "NFC_LLCP permission required for LLCP operations with NFC service");
+
+        if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) {
+
+            int sockeHandle = mGeneratedSocketHandle;
+
+            if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+                NativeLlcpSocket socket;
+
+                socket = mManager.doCreateLlcpSocket(sap, miu, rw, linearBufferLength);
+
+                if (socket != null) {
+                    /* Update the number of socket created */
+                    mNbSocketCreated++;
+                    /* Add the socket into the socket map */
+                    mSocketMap.put(sockeHandle, socket);
+                } else {
+                    /*
+                     * socket creation error - update the socket handle
+                     * generation
+                     */
+                    mGeneratedSocketHandle -= 1;
+
+                    /* Get Error Status */
+                    int errorStatus = mManager.doGetLastError();
+
+                    switch (errorStatus) {
+                        case ErrorCodes.ERROR_BUFFER_TO_SMALL:
+                            return ErrorCodes.ERROR_BUFFER_TO_SMALL;
+                        case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+                            return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES;
+                        default:
+                            return ErrorCodes.ERROR_SOCKET_CREATION;
+                    }
+                }
+            } else {
+
+                /* Check SAP is not already used */
+                if (!CheckSocketSap(sap)) {
+                    return ErrorCodes.ERROR_SAP_USED;
+                }
+
+                /* Check Socket options */
+                if (!CheckSocketOptions(miu, rw, linearBufferLength)) {
+                    return ErrorCodes.ERROR_SOCKET_OPTIONS;
+                }
+
+                NativeLlcpSocket socket = new NativeLlcpSocket(sap, miu, rw);
+
+                /* Add the socket into the socket map */
+                mSocketMap.put(sockeHandle, socket);
+
+                /* Update the number of socket created */
+                mNbSocketCreated++;
+                /* Create new registered socket */
+                RegisteredSocket registeredSocket = new RegisteredSocket(LLCP_SOCKET_TYPE,
+                        sockeHandle, sap, miu, rw, linearBufferLength);
+
+                /* Put this socket into a list of registered socket */
+                mRegisteredSocketList.add(registeredSocket);
+            }
+
+            /* update socket handle generation */
+            mGeneratedSocketHandle++;
+
+            return sockeHandle;
+        } else {
+            /* No socket available */
+            return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    public int deselectSecureElement() throws RemoteException {
+        // Check if NFC is enabled
+        if (!mIsNfcEnabled) {
+            return ErrorCodes.ERROR_NOT_INITIALIZED;
+        }
+
+        if (mSelectedSeId == 0) {
+            return ErrorCodes.ERROR_NO_SE_CONNECTED;
+        }
+
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN,
+                "NFC_ADMIN permission required to deselect NFC Secure Element");
+
+            mManager.doDeselectSecureElement(mSelectedSeId);
+        mNfcSecureElementState = 0;
+            mSelectedSeId = 0;
+        
+        /* Store that a secure element is deselected */
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NFC_SECURE_ELEMENT_ON, 0);
+
+        /* Reset Secure Element ID */
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NFC_SECURE_ELEMENT_ID, 0);
+        
+
+        return ErrorCodes.SUCCESS; 
+    }
+
+    public boolean disable() throws RemoteException {
+        boolean isSuccess = false;
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN,
+                "NFC_ADMIN permission required to disable NFC service");
+        if (isEnabled()) {
+            isSuccess = mManager.deinitialize();
+            if (isSuccess) {
+                mIsNfcEnabled = false;
+            }
+        }
+
+        updateNfcOnSetting();
+
+        return isSuccess;
+    }
+
+    public boolean enable() throws RemoteException {
+        boolean isSuccess = false;
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN,
+                "NFC_ADMIN permission required to enable NFC service");
+        if (!isEnabled()) {
+            reset();
+            isSuccess = _enable();
+        }
+        return isSuccess;
+    }
+
+    private boolean _enable() {
+        boolean isSuccess = mManager.initialize();
+        if (isSuccess) {
+            /* Check persistent properties */
+            checkProperties();
+
+            /* Check Secure Element setting */
+            mNfcSecureElementState = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_SECURE_ELEMENT_ON, 0);
+
+            if (mNfcSecureElementState == 1) {
+
+                int secureElementId = Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.NFC_SECURE_ELEMENT_ID, 0);
+                int[] Se_list = mManager.doGetSecureElementList();
+                if (Se_list != null) {
+                    for (int i = 0; i < Se_list.length; i++) {
+                        if (Se_list[i] == secureElementId) {
+                            mManager.doSelectSecureElement(Se_list[i]);
+                            mSelectedSeId = Se_list[i];
+                            break;
+                        }
+                    }
+                }
+            }
+
+            /* Start polling loop */
+            mManager.enableDiscovery(DISCOVERY_MODE_READER);
+
+            mIsNfcEnabled = true;
+        } else {
+            mIsNfcEnabled = false;
+        }
+
+        updateNfcOnSetting();
+
+        return isSuccess;
+    }
+
+    private void updateNfcOnSetting() {
+        int state;
+
+        if (mIsNfcEnabled) {
+            state = NFC_STATE_ENABLED;
+        } else {
+            state = NFC_STATE_DISABLED;
+        }
+
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_ON, state);
+    }
+
+    private void checkProperties() {
+        int value;
+
+        /* LLCP LTO */
+        value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_LTO,
+                LLCP_LTO_DEFAULT_VALUE);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_LTO, value);
+        mManager.doSetProperties(PROPERTY_LLCP_LTO, value);
+
+        /* LLCP MIU */
+        value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_MIU,
+                LLCP_MIU_DEFAULT_VALUE);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_MIU, value);
+        mManager.doSetProperties(PROPERTY_LLCP_MIU, value);
+
+        /* LLCP WKS */
+        value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_WKS,
+                LLCP_WKS_DEFAULT_VALUE);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_WKS, value);
+        mManager.doSetProperties(PROPERTY_LLCP_WKS, value);
+
+        /* LLCP OPT */
+        value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_OPT,
+                LLCP_OPT_DEFAULT_VALUE);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_OPT, value);
+        mManager.doSetProperties(PROPERTY_LLCP_OPT, value);
+
+        /* NFC READER A */
+        value = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NFC_DISCOVERY_A, 1);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_A,
+                value);
+        mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_A, value);
+
+        /* NFC READER B */
+        value = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NFC_DISCOVERY_B, 1);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_B,
+                value);
+        mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_B, value);
+
+        /* NFC READER F */
+        value = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NFC_DISCOVERY_F, 1);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_F,
+                value);
+        mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_F, value);
+
+        /* NFC READER 15693 */
+        value = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NFC_DISCOVERY_15693, 1);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_15693,
+                value);
+        mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_15693, value);
+
+        /* NFC NFCIP */
+        value = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NFC_DISCOVERY_NFCIP, 1);
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_NFCIP,
+                value);
+        mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_NFCIP, value);
+    }
+
+    public ILlcpConnectionlessSocket getLlcpConnectionlessInterface() throws RemoteException {
+        return mLlcpConnectionlessSocketService;
+    }
+
+    public ILlcpSocket getLlcpInterface() throws RemoteException {
+        return mLlcpSocket;
+    }
+
+    public ILlcpServiceSocket getLlcpServiceInterface() throws RemoteException {
+        return mLlcpServerSocketService;
+    }
+
+    public INfcTag getNfcTagInterface() throws RemoteException {
+        return mNfcTagService;
+    }
+
+    public int getOpenTimeout() throws RemoteException {
+        return mTimeout;
+    }
+
+    public IP2pInitiator getP2pInitiatorInterface() throws RemoteException {
+        return mP2pInitiatorService;
+    }
+
+    public IP2pTarget getP2pTargetInterface() throws RemoteException {
+        return mP2pTargetService;
+    }
+
+    public String getProperties(String param) throws RemoteException {
+        int value;
+
+        if (param == null) {
+            return "Wrong parameter";
+        }
+
+        if (param.equals(PROPERTY_LLCP_LTO_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_LLCP_LTO, 0);
+        } else if (param.equals(PROPERTY_LLCP_MIU_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_LLCP_MIU, 0);
+        } else if (param.equals(PROPERTY_LLCP_WKS_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_LLCP_WKS, 0);
+        } else if (param.equals(PROPERTY_LLCP_OPT_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_LLCP_OPT, 0);
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_A_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_DISCOVERY_A, 0);
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_B_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_DISCOVERY_B, 0);
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_F_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_DISCOVERY_F, 0);
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_NFCIP_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_DISCOVERY_NFCIP, 0);
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_15693_VALUE)) {
+            value = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.NFC_DISCOVERY_15693, 0);
+        } else {
+            return "Unknown property";
+        }
+
+        if (param.equals(PROPERTY_NFC_DISCOVERY_A_VALUE)
+                || param.equals(PROPERTY_NFC_DISCOVERY_B_VALUE)
+                || param.equals(PROPERTY_NFC_DISCOVERY_F_VALUE)
+                || param.equals(PROPERTY_NFC_DISCOVERY_NFCIP_VALUE)
+                || param.equals(PROPERTY_NFC_DISCOVERY_15693_VALUE)) {
+            if (value == 0) {
+                return "false";
+            } else if (value == 1) {
+                return "true";
+            } else {
+                return "Unknown Value";
+            }
+        }else{
+            return "" + value;
+        }
+
+    }
+
+    public int[] getSecureElementList() throws RemoteException {
+        int[] list = null;
+        if (mIsNfcEnabled == true) {
+            list = mManager.doGetSecureElementList();
+        }
+        return list;
+    }
+
+    public int getSelectedSecureElement() throws RemoteException {
+        return mSelectedSeId;
+    }
+
+    public boolean isEnabled() throws RemoteException {
+        return mIsNfcEnabled;
+    }
+
+    public int openP2pConnection() throws RemoteException {
+        // Check if NFC is enabled
+        if (!mIsNfcEnabled) {
+            return ErrorCodes.ERROR_NOT_INITIALIZED;
+        }
+
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW,
+                "NFC_RAW permission required to open NFC P2P connection");
+        if (!mOpenPending) {
+            NativeP2pDevice device;
+            mOpenPending = true;
+            device = mManager.doOpenP2pConnection(mTimeout);
+            if (device != null) {
+                /* add device to the Hmap */
+                mObjectMap.put(device.getHandle(), device);
+                return device.getHandle();
+            } else {
+                mOpenPending = false;
+                /* Restart polling loop for notification */
+                mManager.enableDiscovery(DISCOVERY_MODE_READER);
+                return ErrorCodes.ERROR_IO;
+            }
+        } else {
+            return ErrorCodes.ERROR_BUSY;
+        }
+
+    }
+
+    public int openTagConnection() throws RemoteException {
+        NativeNfcTag tag;
+        // Check if NFC is enabled
+        if (!mIsNfcEnabled) {
+            return ErrorCodes.ERROR_NOT_INITIALIZED;
+        }
+
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW,
+                "NFC_RAW permission required to open NFC Tag connection");
+        if (!mOpenPending) {
+            mOpenPending = true;
+            tag = mManager.doOpenTagConnection(mTimeout);
+            if (tag != null) {
+                mObjectMap.put(tag.getHandle(), tag);
+                return tag.getHandle();
+            } else {
+                mOpenPending = false;
+                /* Restart polling loop for notification */
+                mManager.enableDiscovery(DISCOVERY_MODE_READER);
+                return ErrorCodes.ERROR_IO;
+            }
+        } else {
+            return ErrorCodes.ERROR_BUSY;
+        }
+    }
+
+    public int selectSecureElement(int seId) throws RemoteException {
+        // Check if NFC is enabled
+        if (!mIsNfcEnabled) {
+            return ErrorCodes.ERROR_NOT_INITIALIZED;
+        }
+        
+        if (mSelectedSeId == seId) {
+            return ErrorCodes.ERROR_SE_ALREADY_SELECTED;
+        }
+
+        if (mSelectedSeId != 0) {
+            return ErrorCodes.ERROR_SE_CONNECTED;
+        }
+
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN,
+                "NFC_ADMIN permission required to select NFC Secure Element");
+
+            mSelectedSeId = seId;
+            mManager.doSelectSecureElement(mSelectedSeId);
+
+        /* Store that a secure element is selected */
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NFC_SECURE_ELEMENT_ON, 1);
+
+        /* Store the ID of the Secure Element Selected */
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NFC_SECURE_ELEMENT_ID, mSelectedSeId);
+        
+        mNfcSecureElementState = 1;
+
+        return ErrorCodes.SUCCESS;
+
+    }
+
+    public void setOpenTimeout(int timeout) throws RemoteException {
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW,
+                "NFC_RAW permission required to set NFC connection timeout");
+        mTimeout = timeout;
+    }
+
+    public int setProperties(String param, String value) throws RemoteException {
+        mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN,
+                "NFC_ADMIN permission required to set NFC Properties");
+
+        if (isEnabled()) {
+            return ErrorCodes.ERROR_NFC_ON;
+        }
+
+        int val;
+
+        /* Check params validity */
+        if (param == null || value == null) {
+            return ErrorCodes.ERROR_INVALID_PARAM;
+        }
+
+        if (param.equals(PROPERTY_LLCP_LTO_VALUE)) {
+            val = Integer.parseInt(value);
+
+            /* Check params */
+            if (val > LLCP_LTO_MAX_VALUE)
+                return ErrorCodes.ERROR_INVALID_PARAM;
+
+            /* Store value */
+            Settings.System
+                    .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_LTO, val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_LLCP_LTO, val);
+
+        } else if (param.equals(PROPERTY_LLCP_MIU_VALUE)) {
+            val = Integer.parseInt(value);
+
+            /* Check params */
+            if ((val < LLCP_MIU_DEFAULT_VALUE) || (val > LLCP_MIU_MAX_VALUE))
+                return ErrorCodes.ERROR_INVALID_PARAM;
+
+            /* Store value */
+            Settings.System
+                    .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_MIU, val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_LLCP_MIU, val);
+
+        } else if (param.equals(PROPERTY_LLCP_WKS_VALUE)) {
+            val = Integer.parseInt(value);
+
+            /* Check params */
+            if (val > LLCP_WKS_MAX_VALUE)
+                return ErrorCodes.ERROR_INVALID_PARAM;
+
+            /* Store value */
+            Settings.System
+                    .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_WKS, val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_LLCP_WKS, val);
+
+        } else if (param.equals(PROPERTY_LLCP_OPT_VALUE)) {
+            val = Integer.parseInt(value);
+
+            /* Check params */
+            if (val > LLCP_OPT_MAX_VALUE)
+                return ErrorCodes.ERROR_INVALID_PARAM;
+
+            /* Store value */
+            Settings.System
+                    .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_OPT, val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_LLCP_OPT, val);
+
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_A_VALUE)) {
+
+            /* Check params */
+            if (value.equals("true")) {
+                val = 1;
+            } else if (value.equals("false")) {
+                val = 0;
+            } else {
+                return ErrorCodes.ERROR_INVALID_PARAM;
+            }
+            /* Store value */
+            Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_A,
+                    val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_A, val);
+
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_B_VALUE)) {
+
+            /* Check params */
+            if (value.equals("true")) {
+                val = 1;
+            } else if (value.equals("false")) {
+                val = 0;
+            } else {
+                return ErrorCodes.ERROR_INVALID_PARAM;
+            }
+
+            /* Store value */
+            Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_B,
+                    val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_B, val);
+
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_F_VALUE)) {
+
+            /* Check params */
+            if (value.equals("true")) {
+                val = 1;
+            } else if (value.equals("false")) {
+                val = 0;
+            } else {
+                return ErrorCodes.ERROR_INVALID_PARAM;
+            }
+
+            /* Store value */
+            Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_F,
+                    val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_F, val);
+
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_15693_VALUE)) {
+
+            /* Check params */
+            if (value.equals("true")) {
+                val = 1;
+            } else if (value.equals("false")) {
+                val = 0;
+            } else {
+                return ErrorCodes.ERROR_INVALID_PARAM;
+            }
+
+            /* Store value */
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.NFC_DISCOVERY_15693, val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_15693, val);
+
+        } else if (param.equals(PROPERTY_NFC_DISCOVERY_NFCIP_VALUE)) {
+
+            /* Check params */
+            if (value.equals("true")) {
+                val = 1;
+            } else if (value.equals("false")) {
+                val = 0;
+            } else {
+                return ErrorCodes.ERROR_INVALID_PARAM;
+            }
+
+            /* Store value */
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.NFC_DISCOVERY_NFCIP, val);
+
+            /* Update JNI */
+            mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_NFCIP, val);
+        } else {
+            return ErrorCodes.ERROR_INVALID_PARAM;
+        }
+
+        return ErrorCodes.SUCCESS;
+    }
+
+    // Reset all internals
+    private void reset() {
+
+        // Clear tables
+        mObjectMap.clear();
+        mSocketMap.clear();
+        mRegisteredSocketList.clear();
+
+        // Reset variables
+        mLlcpLinkState = NfcManager.LLCP_LINK_STATE_DEACTIVATED;
+        mNbSocketCreated = 0;
+        mIsNfcEnabled = false;
+        mSelectedSeId = 0;
+        mTimeout = 0;
+        mNfcState = NFC_STATE_DISABLED;
+        mOpenPending = false;
+    }
+
+    private Object findObject(int key) {
+        Object device = null;
+
+        device = mObjectMap.get(key);
+
+        return device;
+    }
+
+    private void RemoveObject(int key) {
+        mObjectMap.remove(key);
+    }
+
+    private Object findSocket(int key) {
+        Object socket = null;
+
+        socket = mSocketMap.get(key);
+
+        return socket;
+    }
+
+    private void RemoveSocket(int key) {
+        mSocketMap.remove(key);
+    }
+
+    private boolean CheckSocketSap(int sap) {
+        /* List of sockets registered */
+        ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator();
+
+        while (it.hasNext()) {
+            RegisteredSocket registeredSocket = it.next();
+
+            if (sap == registeredSocket.mSap) {
+                /* SAP already used */
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean CheckSocketOptions(int miu, int rw, int linearBufferlength) {
+
+        if (rw > LLCP_RW_MAX_VALUE || miu < LLCP_MIU_DEFAULT_VALUE || linearBufferlength < miu) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean CheckSocketServiceName(String sn) {
+
+        /* List of sockets registered */
+        ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator();
+
+        while (it.hasNext()) {
+            RegisteredSocket registeredSocket = it.next();
+
+            if (sn.equals(registeredSocket.mServiceName)) {
+                /* Service Name already used */
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void RemoveRegisteredSocket(int nativeHandle) {
+        /* check if sockets are registered */
+        ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator();
+
+        while (it.hasNext()) {
+            RegisteredSocket registeredSocket = it.next();
+            if (registeredSocket.mHandle == nativeHandle) {
+                /* remove the registered socket from the list */
+                it.remove();
+                Log.d(TAG, "socket removed");
+            }
+        }
+    }
+
+    /*
+     * RegisteredSocket class to store the creation request of socket until the
+     * LLCP link in not activated
+     */
+    private class RegisteredSocket {
+        private int mType;
+
+        private int mHandle;
+
+        private int mSap;
+
+        private int mMiu;
+
+        private int mRw;
+
+        private String mServiceName;
+
+        private int mlinearBufferLength;
+
+        RegisteredSocket(int type, int handle, int sap, String sn, int miu, int rw,
+                int linearBufferLength) {
+            mType = type;
+            mHandle = handle;
+            mSap = sap;
+            mServiceName = sn;
+            mRw = rw;
+            mMiu = miu;
+            mlinearBufferLength = linearBufferLength;
+        }
+
+        RegisteredSocket(int type, int handle, int sap, int miu, int rw, int linearBufferLength) {
+            mType = type;
+            mHandle = handle;
+            mSap = sap;
+            mRw = rw;
+            mMiu = miu;
+            mlinearBufferLength = linearBufferLength;
+        }
+
+        RegisteredSocket(int type, int handle, int sap) {
+            mType = type;
+            mHandle = handle;
+            mSap = sap;
+        }
+    }
+
+    private BroadcastReceiver mNfcServiceReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d(TAG, "Internal NFC Intent received");
+
+            /* LLCP Link deactivation */
+            if (intent.getAction().equals(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION)) {
+                mLlcpLinkState = intent.getIntExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA,
+                        NfcManager.LLCP_LINK_STATE_DEACTIVATED);
+
+                if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_DEACTIVATED) {
+                    /* restart polling loop */
+                    mManager.enableDiscovery(DISCOVERY_MODE_READER);
+                }
+
+            }
+            /* LLCP Link activation */
+            else if (intent.getAction().equals(
+                    NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION)) {
+
+                mLlcpLinkState = intent.getIntExtra(
+                        NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA,
+                        NfcManager.LLCP_LINK_STATE_DEACTIVATED);
+
+                if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+                    /* check if sockets are registered */
+                    ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator();
+
+                    Log.d(TAG, "Nb socket resgistered = " + mRegisteredSocketList.size());
+
+                    while (it.hasNext()) {
+                        RegisteredSocket registeredSocket = it.next();
+
+                        switch (registeredSocket.mType) {
+                            case LLCP_SERVICE_SOCKET_TYPE:
+                                Log.d(TAG, "Registered Llcp Service Socket");
+                                NativeLlcpServiceSocket serviceSocket;
+
+                                serviceSocket = mManager.doCreateLlcpServiceSocket(
+                                        registeredSocket.mSap, registeredSocket.mServiceName,
+                                        registeredSocket.mMiu, registeredSocket.mRw,
+                                        registeredSocket.mlinearBufferLength);
+
+                                if (serviceSocket != null) {
+                                    /* Add the socket into the socket map */
+                                    mSocketMap.put(registeredSocket.mHandle, serviceSocket);
+                                } else {
+                                    /*
+                                     * socket creation error - update the socket
+                                     * handle counter
+                                     */
+                                    mGeneratedSocketHandle -= 1;
+                                }
+                                break;
+
+                            case LLCP_SOCKET_TYPE:
+                                Log.d(TAG, "Registered Llcp Socket");
+                                NativeLlcpSocket clientSocket;
+                                clientSocket = mManager.doCreateLlcpSocket(registeredSocket.mSap,
+                                        registeredSocket.mMiu, registeredSocket.mRw,
+                                        registeredSocket.mlinearBufferLength);
+                                if (clientSocket != null) {
+                                    /* Add the socket into the socket map */
+                                    mSocketMap.put(registeredSocket.mHandle, clientSocket);
+                                } else {
+                                    /*
+                                     * socket creation error - update the socket
+                                     * handle counter
+                                     */
+                                    mGeneratedSocketHandle -= 1;
+                                }
+                                break;
+
+                            case LLCP_CONNECTIONLESS_SOCKET_TYPE:
+                                Log.d(TAG, "Registered Llcp Connectionless Socket");
+                                NativeLlcpConnectionlessSocket connectionlessSocket;
+                                connectionlessSocket = mManager
+                                        .doCreateLlcpConnectionlessSocket(registeredSocket.mSap);
+                                if (connectionlessSocket != null) {
+                                    /* Add the socket into the socket map */
+                                    mSocketMap.put(registeredSocket.mHandle, connectionlessSocket);
+                                } else {
+                                    /*
+                                     * socket creation error - update the socket
+                                     * handle counter
+                                     */
+                                    mGeneratedSocketHandle -= 1;
+                                }
+                                break;
+
+                        }
+                    }
+
+                    /* Remove all registered socket from the list */
+                    mRegisteredSocketList.clear();
+
+                    /* Broadcast Intent Link LLCP activated */
+                    Intent LlcpLinkIntent = new Intent();
+                    LlcpLinkIntent.setAction(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION);
+
+                    LlcpLinkIntent.putExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA,
+                            NfcManager.LLCP_LINK_STATE_ACTIVATED);
+
+                    Log.d(TAG, "Broadcasting LLCP activation");
+                    mContext.sendOrderedBroadcast(LlcpLinkIntent,
+                            android.Manifest.permission.NFC_LLCP);
+                }
+            }            
+            /* Target Deactivated */
+            else if (intent.getAction().equals(
+                    NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)) {
+                if(mOpenPending != false){
+                    mOpenPending = false;
+                }
+                /* Restart polling loop for notification */
+                mManager.enableDiscovery(DISCOVERY_MODE_READER);
+                
+            }
+        }
+    };
+}
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index e3bae56..e1e54fc 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -207,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);
 
@@ -973,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);
 }
 
 
@@ -1288,6 +1286,25 @@
     env->SetIntField(configObj, gConfigurationClassInfo.navigation, config.navigation);
 }
 
+static jboolean android_server_InputManager_nativeTransferTouchFocus(JNIEnv* env,
+        jclass clazz, jobject fromChannelObj, jobject toChannelObj) {
+    if (checkInputManagerUnitialized(env)) {
+        return false;
+    }
+
+    sp<InputChannel> fromChannel =
+            android_view_InputChannel_getInputChannel(env, fromChannelObj);
+    sp<InputChannel> toChannel =
+            android_view_InputChannel_getInputChannel(env, toChannelObj);
+
+    if (fromChannel == NULL || toChannel == NULL) {
+        return false;
+    }
+
+    return gNativeInputManager->getInputManager()->getDispatcher()->
+            transferTouchFocus(fromChannel, toChannel);
+}
+
 static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
     if (checkInputManagerUnitialized(env)) {
         return NULL;
@@ -1336,6 +1353,8 @@
             (void*) android_server_InputManager_nativeGetInputDeviceIds },
     { "nativeGetInputConfiguration", "(Landroid/content/res/Configuration;)V",
             (void*) android_server_InputManager_nativeGetInputConfiguration },
+    { "nativeTransferTouchFocus", "(Landroid/view/InputChannel;Landroid/view/InputChannel;)Z",
+            (void*) android_server_InputManager_nativeTransferTouchFocus },
     { "nativeDump", "()Ljava/lang/String;",
             (void*) android_server_InputManager_nativeDump },
 };
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/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 08194d4..e0eac74 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -444,18 +444,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);
             }
         }
 
@@ -593,7 +598,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;
@@ -636,6 +644,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);
@@ -655,7 +675,6 @@
                             }
                             foregroundCall.switchWith(ringingCall);
                         }
-                        if (newState == Call.State.ACTIVE) call.startAudio();
                         setState(newState);
                     }
                     mOwner.onConnectionStateChanged(SipConnection.this);
@@ -773,11 +792,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);
                 }
@@ -858,8 +879,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/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/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 65da210..dea549a 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -85,14 +85,19 @@
         // http://b/issue?id=2889598
         "http/tests/xmlhttprequest/simple-cross-origin-progress-events.html", // runs webcore into bad state
         // http://b/2982500
+        "canvas/philip/tests/2d.drawImage.broken.html", // blocks test
+        // http://b/2982500
     };
 
     static void fillIgnoreResultList() {
         // This first block of tests are for features for which Android
         // should pass all tests. They are skipped only temporarily.
         // TODO: Fix these failing tests and remove them from this list.
-        ignoreResultList.add("fast/events/touch/basic-multi-touch-events.html"); // Requires multi-touch
-        ignoreResultList.add("fast/events/touch/touch-target.html"); // Requires multi-touch
+        ignoreResultList.add("fast/encoding/char-encoding.html"); // fails in Java HTTP stack, see http://b/issue?id=3047156
+        ignoreResultList.add("fast/encoding/char-decoding.html"); // fails in Java HTTP stack, see http://b/issue?id=3047156
+        ignoreResultList.add("fast/encoding/hanarei-blog32-fc2-com.html"); // fails in Java HTTP stack, see http://b/issue?id=3046986
+        ignoreResultList.add("fast/encoding/mailto-always-utf-8.html"); // Requires waitForPolicyDelegate(), see http://b/issue?id=3043468
+        ignoreResultList.add("fast/encoding/percent-escaping.html"); // fails in Java HTTP stack, see http://b/issue?id=3046984
         ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
         ignoreResultList.add("http/tests/appcache/fallback.html"); // http://b/issue?id=2713004
         ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
@@ -107,8 +112,13 @@
         ignoreResultList.add("storage/transaction-error-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
         ignoreResultList.add("storage/transaction-success-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
 
-        // Expected failures due to unsupported features.
+        // Expected failures due to unsupported features or tests unsuitable for Android.
+        ignoreResultList.add("fast/encoding/char-decoding-mac.html"); // Mac-specific encodings (also marked Won't Fix in Chromium, bug 7388)
+        ignoreResultList.add("fast/encoding/char-encoding-mac.html"); // Mac-specific encodings (also marked Won't Fix in Chromium, bug 7388)
+        ignoreResultList.add("fast/encoding/idn-security.html"); // Mac-specific IDN checks (also marked Won't Fix in Chromium, bug 21814)
+        ignoreResultList.add("fast/events/touch/basic-multi-touch-events.html"); // Requires multi-touch gestures not supported by Android system
         ignoreResultList.add("fast/events/touch/touch-coords-in-zoom-and-scroll.html"); // Requires eventSender.zoomPageIn(),zoomPageOut()
+        ignoreResultList.add("fast/events/touch/touch-target.html"); // Requires multi-touch gestures not supported by Android system
         ignoreResultList.add("fast/workers"); // workers not supported
         ignoreResultList.add("http/tests/eventsource/workers"); // workers not supported
         ignoreResultList.add("http/tests/workers"); // workers not supported
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 40af8c0..19815fd 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -145,6 +145,10 @@
         // WebView::setJsFlags is noop in JSC build.
         mWebView.setJsFlags("--expose_gc");
 
+        // Always send multitouch events to Webkit since the layout test
+        // is only for the Webkit not the browser's UI.
+        mWebView.setDeferMultiTouch(true);
+
         mHandler = new AsyncHandler();
 
         Intent intent = getIntent();
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
index 716086b..383d782 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
@@ -31,6 +31,7 @@
 	
     WebViewEventSender(WebView webView) {
         mWebView = webView;
+        mWebView.getSettings().setBuiltInZoomControls(true);
         mTouchPoints = new Vector<TouchPoint>();
     }
 	
@@ -170,70 +171,128 @@
 	}
 
     public void touchStart() {
-        // We only support single touch so examine the first touch point only.
-        // If multi touch is enabled in the future, we need to re-examine this to send
-        // all the touch points with the event.
-        TouchPoint tp = mTouchPoints.get(0);
-
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        tp.setDownTime(SystemClock.uptimeMillis());
-        MotionEvent event = MotionEvent.obtain(tp.downTime(), tp.downTime(),
-                MotionEvent.ACTION_DOWN, tp.getX(), tp.getY(), mTouchMetaState);
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+        long downTime = SystemClock.uptimeMillis();
+
+        for (int i = 0; i < numPoints; ++i) {
+            pointerIds[i] = mTouchPoints.get(i).getId();
+            pointerCoords[i] = new MotionEvent.PointerCoords();
+            pointerCoords[i].x = mTouchPoints.get(i).getX();
+            pointerCoords[i].y = mTouchPoints.get(i).getY();
+            mTouchPoints.get(i).setDownTime(downTime);
+        }
+
+        MotionEvent event = MotionEvent.obtain(downTime, downTime,
+            MotionEvent.ACTION_DOWN, numPoints, pointerIds, pointerCoords,
+            mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
+
         mWebView.onTouchEvent(event);
     }
 
     public void touchMove() {
-        TouchPoint tp = mTouchPoints.get(0);
-
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        if (!tp.hasMoved()) {
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+        int numMovedPoints = 0;
+        for (int i = 0; i < numPoints; ++i) {
+            TouchPoint tp = mTouchPoints.get(i);
+            if (tp.hasMoved()) {
+                pointerIds[numMovedPoints] = mTouchPoints.get(i).getId();
+                pointerCoords[i] = new MotionEvent.PointerCoords();
+                pointerCoords[numMovedPoints].x = mTouchPoints.get(i).getX();
+                pointerCoords[numMovedPoints].y = mTouchPoints.get(i).getY();
+                ++numMovedPoints;
+                tp.setMoved(false);
+            }
+        }
+
+        if (numMovedPoints == 0) {
             return;
         }
 
-        MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
-                MotionEvent.ACTION_MOVE, tp.getX(), tp.getY(), mTouchMetaState);
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(),
+                SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE,
+                numMovedPoints, pointerIds, pointerCoords,
+                mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
         mWebView.onTouchEvent(event);
-
-        tp.setMoved(false);
     }
 
     public void touchEnd() {
-        TouchPoint tp = mTouchPoints.get(0);
-
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
-                MotionEvent.ACTION_UP, tp.getX(), tp.getY(), mTouchMetaState);
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+
+        for (int i = 0; i < numPoints; ++i) {
+            pointerIds[i] = mTouchPoints.get(i).getId();
+            pointerCoords[i] = new MotionEvent.PointerCoords();
+            pointerCoords[i].x = mTouchPoints.get(i).getX();
+            pointerCoords[i].y = mTouchPoints.get(i).getY();
+        }
+
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(),
+                SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
+                numPoints, pointerIds, pointerCoords,
+                mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
         mWebView.onTouchEvent(event);
 
-        if (tp.isReleased()) {
-            mTouchPoints.remove(0);
+        for (int i = numPoints - 1; i >= 0; --i) {  // remove released points.
+            TouchPoint tp = mTouchPoints.get(i);
+            if (tp.isReleased()) {
+              mTouchPoints.remove(i);
+            }
         }
     }
 
     public void touchCancel() {
-        TouchPoint tp = mTouchPoints.get(0);
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        if (tp.cancelled()) {
-            MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
-                    MotionEvent.ACTION_CANCEL, tp.getX(), tp.getY(), mTouchMetaState);
-            mWebView.onTouchEvent(event);
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+        long cancelTime = SystemClock.uptimeMillis();
+        int numCanceledPoints = 0;
+
+        for (int i = 0; i < numPoints; ++i) {
+            TouchPoint tp = mTouchPoints.get(i);
+            if (tp.cancelled()) {
+                pointerIds[numCanceledPoints] = mTouchPoints.get(i).getId();
+                pointerCoords[numCanceledPoints] = new MotionEvent.PointerCoords();
+                pointerCoords[numCanceledPoints].x = mTouchPoints.get(i).getX();
+                pointerCoords[numCanceledPoints].y = mTouchPoints.get(i).getY();
+                ++numCanceledPoints;
+            }
         }
+
+        if (numCanceledPoints == 0) {
+            return;
+        }
+
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(),
+            SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL,
+            numCanceledPoints, pointerIds, pointerCoords,
+            mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
+
+        mWebView.onTouchEvent(event);
     }
 
     public void cancelTouchPoint(int id) {
-        TouchPoint tp = mTouchPoints.get(0);
+        TouchPoint tp = mTouchPoints.get(id);
         if (tp == null) {
             return;
         }
@@ -242,14 +301,19 @@
     }
 
     public void addTouchPoint(int x, int y) {
-        mTouchPoints.add(new TouchPoint(contentsToWindowX(x), contentsToWindowY(y)));
-        if (mTouchPoints.size() > 1) {
-            Log.w(LOGTAG, "Adding more than one touch point, but multi touch is not supported!");
+        final int numPoints = mTouchPoints.size();
+        int id;
+        if (numPoints == 0) {
+          id = 0;
+        } else {
+          id = mTouchPoints.get(numPoints - 1).getId() + 1;
         }
+
+        mTouchPoints.add(new TouchPoint(id, contentsToWindowX(x), contentsToWindowY(y)));
     }
 
-    public void updateTouchPoint(int id, int x, int y) {
-        TouchPoint tp = mTouchPoints.get(0);
+    public void updateTouchPoint(int i, int x, int y) {
+        TouchPoint tp = mTouchPoints.get(i);
         if (tp == null) {
             return;
         }
@@ -276,7 +340,7 @@
     }
 
     public void releaseTouchPoint(int id) {
-        TouchPoint tp = mTouchPoints.get(0);
+        TouchPoint tp = mTouchPoints.get(id);
         if (tp == null) {
             return;
         }
@@ -305,6 +369,7 @@
     private int mouseY;
 
     private class TouchPoint {
+        private int mId;
         private int mX;
         private int mY;
         private long mDownTime;
@@ -312,7 +377,8 @@
         private boolean mMoved;
         private boolean mCancelled;
 
-        public TouchPoint(int x, int y) {
+        public TouchPoint(int id, int x, int y) {
+            mId = id;
             mX = x;
             mY = y;
             mReleased = false;
@@ -332,6 +398,7 @@
         public void setMoved(boolean moved) { mMoved = moved; }
         public boolean hasMoved() { return mMoved; }
 
+        public int getId() { return mId; }
         public int getX() { return mX; }
         public int getY() { return mY; }
 
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
index fd76e4a9..303a054 100755
--- a/tests/DumpRenderTree2/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -12,6 +12,7 @@
 import logging
 import optparse
 import os
+import re
 import sys
 import subprocess
 import tempfile
@@ -44,27 +45,33 @@
   os.system(cmd);
 
   # Run the tests in path
-  cmd = "adb shell am instrument "
+  adb_cmd = "adb"
+  if options.serial:
+    adb_cmd += " -s " + options.serial
+  cmd = adb_cmd + " shell am instrument "
   cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
   cmd += "-e path \"" + path + "\" "
-  cmd +="-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+  cmd += "-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
 
   logging.info("Running the tests...")
-  subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+  (stdoutdata, stderrdata) = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+  if re.search("^INSTRUMENTATION_STATUS_CODE: -1", stdoutdata, re.MULTILINE) != None:
+    logging.info("Failed to run the tests. Is DumpRenderTree2 installed on the device?")
+    return
 
   logging.info("Downloading the summaries...")
 
   # Download the txt summary to tmp folder
   summary_txt_tmp_path = os.path.join(tmpdir, SUMMARY_TXT)
   cmd = "rm -f " + summary_txt_tmp_path + ";"
-  cmd += "adb pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path
-  subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+  cmd += adb_cmd + " pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path
+  subprocess.Popen(cmd, shell=True).wait()
 
   # Download the html summary to tmp folder
   details_html_tmp_path = os.path.join(tmpdir, DETAILS_HTML)
   cmd = "rm -f " + details_html_tmp_path + ";"
-  cmd += "adb pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path
-  subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+  cmd += adb_cmd + " pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path
+  subprocess.Popen(cmd, shell=True).wait()
 
   # Print summary to console
   logging.info("All done.\n")
@@ -82,5 +89,6 @@
                            help="Show the results the host's default web browser, default=true")
   option_parser.add_option("", "--tests-root-directory",
                            help="The directory from which to take the tests, default is external/webkit/LayoutTests in this checkout of the Android tree")
+  option_parser.add_option("-s", "--serial", default=None, help="Specify the serial number of device to run test on")
   options, args = option_parser.parse_args();
   main(options, args);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
index 93e6137..68bcf11 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
@@ -58,6 +58,7 @@
 
     public static class TouchPoint {
         WebView mWebView;
+        private int mId;
         private int mX;
         private int mY;
         private long mDownTime;
@@ -65,12 +66,17 @@
         private boolean mMoved = false;
         private boolean mCancelled = false;
 
-        public TouchPoint(WebView webView, int x, int y) {
+        public TouchPoint(WebView webView, int id, int x, int y) {
             mWebView = webView;
+            mId = id;
             mX = scaleX(x);
             mY = scaleY(y);
         }
 
+        public int getId() {
+          return mId;
+        }
+
         public int getX() {
             return mX;
         }
@@ -136,9 +142,9 @@
     private Handler mEventSenderHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            TouchPoint touchPoint;
             Bundle bundle;
-            KeyEvent event;
+            MotionEvent event;
+            long ts;
 
             switch (msg.what) {
                 case MSG_ENABLE_DOM_UI_EVENT_LOGGING:
@@ -171,82 +177,62 @@
                 /** MOUSE */
 
                 case MSG_MOUSE_DOWN:
-                    /** TODO: Implement */
+                    ts = SystemClock.uptimeMillis();
+                    event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mMouseX, mMouseY, 0);
+                    mWebView.onTouchEvent(event);
                     break;
 
                 case MSG_MOUSE_UP:
-                    /** TODO: Implement */
+                    ts = SystemClock.uptimeMillis();
+                    event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mMouseX, mMouseY, 0);
+                    mWebView.onTouchEvent(event);
                     break;
 
                 case MSG_MOUSE_CLICK:
-                    /** TODO: Implement */
+                    mouseDown();
+                    mouseUp();
                     break;
 
                 case MSG_MOUSE_MOVE_TO:
-                    int x = msg.arg1;
-                    int y = msg.arg2;
-
-                    event = null;
-                    if (x > mMouseX) {
-                        event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
-                    } else if (x < mMouseX) {
-                        event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
-                    }
-                    if (event != null) {
-                        mWebView.onKeyDown(event.getKeyCode(), event);
-                        mWebView.onKeyUp(event.getKeyCode(), event);
-                    }
-
-                    event = null;
-                    if (y > mMouseY) {
-                        event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
-                    } else if (y < mMouseY) {
-                        event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
-                    }
-                    if (event != null) {
-                        mWebView.onKeyDown(event.getKeyCode(), event);
-                        mWebView.onKeyUp(event.getKeyCode(), event);
-                    }
-
-                    mMouseX = x;
-                    mMouseY = y;
+                    mMouseX = msg.arg1;
+                    mMouseY = msg.arg2;
                     break;
 
                 /** TOUCH */
 
                 case MSG_ADD_TOUCH_POINT:
-                    getTouchPoints().add(new TouchPoint(mWebView,
-                            msg.arg1, msg.arg2));
-                    if (getTouchPoints().size() > 1) {
-                        Log.w(LOG_TAG + "::MSG_ADD_TOUCH_POINT", "Added more than one touch point");
+                    int numPoints = getTouchPoints().size();
+                    int id;
+                    if (numPoints == 0) {
+                        id = 0;
+                    } else {
+                        id = getTouchPoints().get(numPoints - 1).getId() + 1;
                     }
+                    getTouchPoints().add(new TouchPoint(mWebView, id,
+                            msg.arg1, msg.arg2));
                     break;
 
                 case MSG_TOUCH_START:
-                    /**
-                     * FIXME: At the moment we don't support multi-touch. Hence, we only examine
-                     * the first touch point. In future this method will need rewriting.
-                     */
                     if (getTouchPoints().isEmpty()) {
                         return;
                     }
-                    touchPoint = getTouchPoints().get(0);
-
-                    touchPoint.setDownTime(SystemClock.uptimeMillis());
-                    executeTouchEvent(touchPoint, MotionEvent.ACTION_DOWN);
+                    for (int i = 0; i < getTouchPoints().size(); ++i) {
+                        getTouchPoints().get(i).setDownTime(SystemClock.uptimeMillis());
+                    }
+                    executeTouchEvent(MotionEvent.ACTION_DOWN);
                     break;
 
                 case MSG_UPDATE_TOUCH_POINT:
                     bundle = (Bundle)msg.obj;
 
-                    int id = bundle.getInt("id");
-                    if (id >= getTouchPoints().size()) {
+                    int index = bundle.getInt("id");
+                    if (index >= getTouchPoints().size()) {
                         Log.w(LOG_TAG + "::MSG_UPDATE_TOUCH_POINT", "TouchPoint out of bounds: "
-                                + id);
+                                + index);
                         break;
                     }
 
-                    getTouchPoints().get(id).move(bundle.getInt("x"), bundle.getInt("y"));
+                    getTouchPoints().get(index).move(bundle.getInt("x"), bundle.getInt("y"));
                     break;
 
                 case MSG_TOUCH_MOVE:
@@ -257,13 +243,10 @@
                     if (getTouchPoints().isEmpty()) {
                         return;
                     }
-                    touchPoint = getTouchPoints().get(0);
-
-                    if (!touchPoint.hasMoved()) {
-                        return;
+                    executeTouchEvent(MotionEvent.ACTION_MOVE);
+                    for (int i = 0; i < getTouchPoints().size(); ++i) {
+                        getTouchPoints().get(i).resetHasMoved();
                     }
-                    executeTouchEvent(touchPoint, MotionEvent.ACTION_MOVE);
-                    touchPoint.resetHasMoved();
                     break;
 
                 case MSG_CANCEL_TOUCH_POINT:
@@ -284,11 +267,7 @@
                     if (getTouchPoints().isEmpty()) {
                         return;
                     }
-                    touchPoint = getTouchPoints().get(0);
-
-                    if (touchPoint.isCancelled()) {
-                        executeTouchEvent(touchPoint, MotionEvent.ACTION_CANCEL);
-                    }
+                    executeTouchEvent(MotionEvent.ACTION_CANCEL);
                     break;
 
                 case MSG_RELEASE_TOUCH_POINT:
@@ -309,12 +288,12 @@
                     if (getTouchPoints().isEmpty()) {
                         return;
                     }
-                    touchPoint = getTouchPoints().get(0);
-
-                    executeTouchEvent(touchPoint, MotionEvent.ACTION_UP);
-                    if (touchPoint.isReleased()) {
-                        getTouchPoints().remove(0);
-                        touchPoint = null;
+                    executeTouchEvent(MotionEvent.ACTION_UP);
+                    // remove released points.
+                    for (int i = getTouchPoints().size() - 1; i >= 0; --i) {
+                        if (getTouchPoints().get(i).isReleased()) {
+                            getTouchPoints().remove(i);
+                        }
                     }
                     break;
 
@@ -462,10 +441,48 @@
         return mTouchPoints;
     }
 
-    private void executeTouchEvent(TouchPoint touchPoint, int action) {
-        MotionEvent event =
-                MotionEvent.obtain(touchPoint.getDownTime(), SystemClock.uptimeMillis(),
-                action, touchPoint.getX(), touchPoint.getY(), mTouchMetaState);
+    private void executeTouchEvent(int action) {
+        int numPoints = getTouchPoints().size();
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+
+        for (int i = 0; i < numPoints; ++i) {
+            boolean isNeeded = false;
+            switch(action) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_UP:
+                isNeeded = true;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                isNeeded = getTouchPoints().get(i).hasMoved();
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                isNeeded = getTouchPoints().get(i).isCancelled();
+                break;
+            default:
+                Log.w(LOG_TAG + "::executeTouchEvent(),", "action not supported:" + action);
+                break;
+            }
+
+            numPoints = 0;
+            if (isNeeded) {
+                pointerIds[numPoints] = getTouchPoints().get(i).getId();
+                pointerCoords[numPoints] = new MotionEvent.PointerCoords();
+                pointerCoords[numPoints].x = getTouchPoints().get(i).getX();
+                pointerCoords[numPoints].y = getTouchPoints().get(i).getY();
+                ++numPoints;
+            }
+        }
+
+        if (numPoints == 0) {
+            return;
+        }
+
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).getDownTime(),
+                SystemClock.uptimeMillis(), action,
+                numPoints, pointerIds, pointerCoords,
+                mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
+
         mWebView.onTouchEvent(event);
     }
 
@@ -560,4 +577,4 @@
 
         return KeyEvent.KEYCODE_UNKNOWN;
     }
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
index 4438811..4f9a737 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
@@ -223,10 +223,9 @@
         try {
             return getHttpClient().execute(httpRequest, handler);
         } catch (IOException e) {
-            Log.e(LOG_TAG, "url=" + url, e);
+            Log.e(LOG_TAG, "getLayoutTestsDirContents(): HTTP GET failed for URL " + url);
+            return null;
         }
-
-        return new LinkedList<String>();
     }
 
     public static void closeInputStream(InputStream inputStream) {
@@ -248,4 +247,4 @@
             Log.e(LOG_TAG, "Couldn't close stream!", e);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index 30d255a..97d7cca 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -377,11 +377,14 @@
         webView.setTouchInterval(-1);
 
         webView.clearCache(true);
+        webView.setDeferMultiTouch(true);
 
         WebSettings webViewSettings = webView.getSettings();
         webViewSettings.setAppCacheEnabled(true);
         webViewSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
-        webViewSettings.setAppCacheMaxSize(Long.MAX_VALUE);
+        // Use of larger values causes unexplained AppCache database corruption.
+        // TODO: Investigate what's really going on here.
+        webViewSettings.setAppCacheMaxSize(100 * 1024 * 1024);
         webViewSettings.setJavaScriptEnabled(true);
         webViewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
         webViewSettings.setSupportMultipleWindows(true);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index 17e19d0..f42dc86 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -267,7 +267,7 @@
             bytes = FsUtils.readDataFromUrl(FileFilter.getUrl(relativePath));
         }
 
-        mLastExpectedResultPathFetched = relativePath;
+        mLastExpectedResultPathFetched = bytes == null ? null : relativePath;
         return bytes;
     }
 
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 25c5ad5..8d01a53 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -539,7 +539,11 @@
         String textSource = result.getExpectedTextResultPath();
         String imageSource = result.getExpectedImageResultPath();
 
-        if (textSource != null) {
+        if (textSource == null) {
+            // Show if a text result is missing. We may want to revisit this decision when we add
+            // support for image results.
+            html.append("<span class=\"source\">Expected textual result missing</span>");
+        } else {
             html.append("<span class=\"source\">Expected textual result from: ");
             html.append("<a href=\"" + ForwarderManager.getHostSchemePort(false) + "LayoutTests/" +
                     textSource + "\"");
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
index e0f14500..0e7d47a 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.List;
 
 /**
  * A Thread that is responsible for generating a lists of tests to run.
@@ -35,7 +36,7 @@
     private FileFilter mFileFilter;
 
     /**
-     * A relative path to the folder with the tests we want to run or particular test.
+     * A relative path to the directory with the tests we want to run or particular test.
      * Used up to and including preloadTests().
      */
     private String mRelativePath;
@@ -67,40 +68,44 @@
     }
 
     /**
-     * Loads all the tests from the given folders and all the subfolders
+     * Loads all the tests from the given directories and all the subdirectories
      * into mTestsList.
      *
      * @param dirRelativePath
      */
-    private void loadTestsFromUrl(String dirRelativePath) {
-        LinkedList<String> foldersList = new LinkedList<String>();
-        foldersList.add(dirRelativePath);
+    private void loadTestsFromUrl(String rootRelativePath) {
+        LinkedList<String> directoriesList = new LinkedList<String>();
+        directoriesList.add(rootRelativePath);
 
         String relativePath;
         String itemName;
-        while (!foldersList.isEmpty()) {
-            relativePath = foldersList.removeFirst();
+        while (!directoriesList.isEmpty()) {
+            relativePath = directoriesList.removeFirst();
 
-            for (String folderRelativePath : FsUtils.getLayoutTestsDirContents(relativePath,
-                    false, true)) {
-                itemName = new File(folderRelativePath).getName();
-                if (FileFilter.isTestDir(itemName)) {
-                    foldersList.add(folderRelativePath);
+            List<String> dirRelativePaths = FsUtils.getLayoutTestsDirContents(relativePath, false, true);
+            if (dirRelativePaths != null) {
+                for (String dirRelativePath : dirRelativePaths) {
+                    itemName = new File(dirRelativePath).getName();
+                    if (FileFilter.isTestDir(itemName)) {
+                        directoriesList.add(dirRelativePath);
+                    }
                 }
             }
 
-            for (String testRelativePath : FsUtils.getLayoutTestsDirContents(relativePath,
-                    false, false)) {
-                itemName = new File(testRelativePath).getName();
-                if (FileFilter.isTestFile(itemName)) {
-                    /** We chose to skip all the tests that are expected to crash. */
-                    if (!mFileFilter.isCrash(testRelativePath)) {
-                        mTestsList.add(testRelativePath);
-                    } else {
-                        /**
-                         * TODO: Summarizer is now in service - figure out how to send the info.
-                         * Previously: mSummarizer.addSkippedTest(relativePath);
-                         */
+            List<String> testRelativePaths = FsUtils.getLayoutTestsDirContents(relativePath, false, false);
+            if (testRelativePaths != null) {
+                for (String testRelativePath : testRelativePaths) {
+                    itemName = new File(testRelativePath).getName();
+                    if (FileFilter.isTestFile(itemName)) {
+                        /** We choose to skip all the tests that are expected to crash. */
+                        if (!mFileFilter.isCrash(testRelativePath)) {
+                            mTestsList.add(testRelativePath);
+                        } else {
+                            /**
+                             * TODO: Summarizer is now in service - figure out how to send the info.
+                             * Previously: mSummarizer.addSkippedTest(relativePath);
+                             */
+                        }
                     }
                 }
             }
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
index f835b6a..3d2b98b 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
@@ -87,20 +87,26 @@
 
     @Override
     public ResultCode getResultCode() {
-        if (mResultCode != null) {
-            return mResultCode;
-        }
-
-        if (mExpectedResult == null) {
-            mResultCode = AbstractResult.ResultCode.NO_EXPECTED_RESULT;
-        } else if (!mExpectedResult.equals(mActualResult)) {
-            mResultCode = AbstractResult.ResultCode.RESULTS_DIFFER;
-        } else {
-            mResultCode = AbstractResult.ResultCode.RESULTS_MATCH;
+        if (mResultCode == null) {
+            mResultCode = resultsMatch() ? AbstractResult.ResultCode.RESULTS_MATCH
+                    : AbstractResult.ResultCode.RESULTS_DIFFER;
         }
         return mResultCode;
     }
 
+    private boolean resultsMatch() {
+        assert mExpectedResult != null;
+        assert mActualResult != null;
+        // Trim leading and trailing empty lines, as other WebKit platforms do.
+        String leadingEmptyLines = "^\\n+";
+        String trailingEmptyLines = "\\n+$";
+        String trimmedExpectedResult = mExpectedResult.replaceFirst(leadingEmptyLines, "")
+                .replaceFirst(trailingEmptyLines, "");
+        String trimmedActualResult = mActualResult.replaceFirst(leadingEmptyLines, "")
+                .replaceFirst(trailingEmptyLines, "");
+        return trimmedExpectedResult.equals(trimmedActualResult);
+    }
+
     @Override
     public boolean didCrash() {
         return false;
@@ -125,7 +131,7 @@
     public String getActualTextResult() {
         String additionalTextResultString = getAdditionalTextOutputString();
         if (additionalTextResultString != null) {
-            return additionalTextResultString+ mActualResult;
+            return additionalTextResultString + mActualResult;
         }
 
         return mActualResult;
@@ -159,11 +165,16 @@
 
     @Override
     public void setExpectedTextResult(String expectedResult) {
-        mExpectedResult = expectedResult;
+        // For text results, we use an empty string for the expected result when none is
+        // present, as other WebKit platforms do.
+        mExpectedResult = expectedResult == null ? "" : expectedResult;
     }
 
     @Override
     public String getDiffAsHtml() {
+        assert mExpectedResult != null;
+        assert mActualResult != null;
+
         StringBuilder html = new StringBuilder();
         html.append("<table class=\"visual_diff\">");
         html.append("    <tr class=\"headers\">");
@@ -172,11 +183,7 @@
         html.append("        <td colspan=\"2\">Actual result:</td>");
         html.append("    </tr>");
 
-        if (mExpectedResult == null || mActualResult == null) {
-            appendNullsHtml(html);
-        } else {
-            appendDiffHtml(html);
-        }
+        appendDiffHtml(html);
 
         html.append("    <tr class=\"footers\">");
         html.append("        <td colspan=\"2\"></td>");
@@ -201,36 +208,15 @@
 
         VisualDiffUtils.generateExpectedResultLines(diffs, expectedLineNums, expectedLines);
         VisualDiffUtils.generateActualResultLines(diffs, actualLineNums, actualLines);
+        // TODO: We should use a map for each line number and lines pair.
+        assert expectedLines.size() == expectedLineNums.size();
+        assert actualLines.size() == actualLineNums.size();
+        assert expectedLines.size() == actualLines.size();
 
         html.append(VisualDiffUtils.getHtml(expectedLineNums, expectedLines,
                 actualLineNums, actualLines));
     }
 
-    private void appendNullsHtml(StringBuilder html) {
-        /** TODO: Create a separate row for each line of not null result */
-        html.append("    <tr class=\"results\">");
-        html.append("    <td class=\"line_count\">");
-        html.append("    </td>");
-        html.append("    <td class=\"line\">");
-        if (mExpectedResult == null) {
-            html.append("Expected result was NULL");
-        } else {
-            html.append(mExpectedResult.replace("\n", "<br />"));
-        }
-        html.append("        </td>");
-        html.append("        <td class=\"space\"></td>");
-        html.append("    <td class=\"line_count\">");
-        html.append("    </td>");
-        html.append("    <td class=\"line\">");
-        if (mActualResult == null) {
-            html.append("Actual result was NULL");
-        } else {
-            html.append(mActualResult.replace("\n", "<br />"));
-        }
-        html.append("        </td>");
-        html.append("    </tr>");
-    }
-
     @Override
     public TestType getType() {
         return TestType.TEXT;
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
index 26bf75c..d7f7313 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
@@ -68,35 +68,38 @@
 
         String line = "";
         int i = 1;
-        for (diff_match_patch.Diff diff : diffs) {
+        diff_match_patch.Diff diff;
+        int size = diffs.size();
+        boolean isLastDiff;
+        for (int j = 0; j < size; j++) {
+            diff = diffs.get(j);
+            isLastDiff = j == size - 1;
             switch (diff.operation) {
                 case DELETE:
-                    line = processDiff(diff, lineNums, lines, line, i, delSpan);
+                    line = processDiff(diff, lineNums, lines, line, i, delSpan, isLastDiff);
                     if (line.equals("")) {
                         i++;
                     }
                     break;
 
                 case INSERT:
-                    if (diff.text.endsWith("\n")) {
-                        lineNums.add(DONT_PRINT_LINE_NUMBER);
-                        lines.add("");
+                    // If the line is currently empty and this insertion is the entire line, the
+                    // expected line is absent, so it has no line number.
+                    if (diff.text.endsWith("\n") || isLastDiff) {
+                        lineNums.add(line.equals("") ? DONT_PRINT_LINE_NUMBER : i++);
+                        lines.add(line);
+                        line = "";
                     }
                     break;
 
                 case EQUAL:
-                    line = processDiff(diff, lineNums, lines, line, i, eqlSpan);
+                    line = processDiff(diff, lineNums, lines, line, i, eqlSpan, isLastDiff);
                     if (line.equals("")) {
                         i++;
                     }
                     break;
             }
         }
-
-        if (!line.isEmpty()) {
-            lines.add(line);
-            lineNums.add(i);
-        }
     }
 
     public static void generateActualResultLines(LinkedList<diff_match_patch.Diff> diffs,
@@ -106,35 +109,38 @@
 
         String line = "";
         int i = 1;
-        for (diff_match_patch.Diff diff : diffs) {
+        diff_match_patch.Diff diff;
+        int size = diffs.size();
+        boolean isLastDiff;
+        for (int j = 0; j < size; j++) {
+            diff = diffs.get(j);
+            isLastDiff = j == size - 1;
             switch (diff.operation) {
                 case INSERT:
-                    line = processDiff(diff, lineNums, lines, line, i, insSpan);
+                    line = processDiff(diff, lineNums, lines, line, i, insSpan, isLastDiff);
                     if (line.equals("")) {
                         i++;
                     }
                     break;
 
                 case DELETE:
-                    if (diff.text.endsWith("\n")) {
-                        lineNums.add(DONT_PRINT_LINE_NUMBER);
-                        lines.add("");
+                    // If the line is currently empty and deletion is the entire line, the
+                    // actual line is absent, so it has no line number.
+                    if (diff.text.endsWith("\n") || isLastDiff) {
+                        lineNums.add(line.equals("") ? DONT_PRINT_LINE_NUMBER : i++);
+                        lines.add(line);
+                        line = "";
                     }
                     break;
 
                 case EQUAL:
-                    line = processDiff(diff, lineNums, lines, line, i, eqlSpan);
+                    line = processDiff(diff, lineNums, lines, line, i, eqlSpan, isLastDiff);
                     if (line.equals("")) {
                         i++;
                     }
                     break;
             }
         }
-
-        if (!line.isEmpty()) {
-            lines.add(line);
-            lineNums.add(i);
-        }
     }
 
     /**
@@ -147,15 +153,16 @@
      * @param line
      * @param i
      * @param begSpan
+     * @param forceOutputLine Force the current line to be output
      * @return
      */
     public static String processDiff(diff_match_patch.Diff diff, LinkedList<Integer> lineNums,
-            LinkedList<String> lines, String line, int i, String begSpan) {
+            LinkedList<String> lines, String line, int i, String begSpan, boolean forceOutputLine) {
         String endSpan = "</span>";
         String br = "&nbsp;";
 
-        if (diff.text.endsWith("\n")) {
-            lineNums.add(i++);
+        if (diff.text.endsWith("\n") || forceOutputLine) {
+            lineNums.add(i);
             /** TODO: Think of better way to replace stuff */
             line += begSpan + diff.text.replace("  ", "&nbsp;&nbsp;")
                     + endSpan + br;
@@ -204,4 +211,4 @@
         }
         return html.toString();
     }
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
index 086ff59..224509d 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
@@ -40,15 +40,10 @@
      * remote machine. This can be achieved by calling configureSocket()
      *
      * @return a socket that can be configured to link to remote machine
+     * @throws IOException
      */
-    public static Socket createSocket() {
-        Socket socket = null;
-        try {
-            socket = new Socket(ADB_HOST, ADB_PORT);
-        } catch (IOException e) {
-            Log.e(LOG_TAG, "Creation failed.", e);
-        }
-        return socket;
+    public static Socket createSocket() throws IOException{
+        return new Socket(ADB_HOST, ADB_PORT);
     }
 
     /**
@@ -72,9 +67,9 @@
         outputStream.write(cmd.getBytes());
         int read = inputStream.read(buf);
         if (read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) {
-            Log.w(LOG_TAG, "adb cmd faild.");
+            Log.w(LOG_TAG, "adb cmd failed.");
             return false;
         }
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
index 4f01dae..f19cd41 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
@@ -93,7 +93,8 @@
 
     private OnFinishedCallback mOnFinishedCallback;
 
-    public ConnectionHandler(String remoteMachineIp, int port, Socket fromSocket, Socket toSocket) {
+    public ConnectionHandler(String remoteMachineIp, int port, Socket fromSocket, Socket toSocket)
+            throws IOException {
         mRemoteMachineIpAddress = remoteMachineIp;
         mPort = port;
 
@@ -105,14 +106,12 @@
             mToSocketInputStream = mToSocket.getInputStream();
             mFromSocketOutputStream = mFromSocket.getOutputStream();
             mToSocketOutputStream = mToSocket.getOutputStream();
-            if (!AdbUtils.configureConnection(mToSocketInputStream, mToSocketOutputStream,
-                    mRemoteMachineIpAddress, mPort)) {
-                throw new IOException("Configuring socket failed!");
-            }
+            AdbUtils.configureConnection(mToSocketInputStream, mToSocketOutputStream,
+                    mRemoteMachineIpAddress, mPort);
         } catch (IOException e) {
             Log.e(LOG_TAG, "Unable to start ConnectionHandler", e);
             closeStreams();
-            return;
+            throw e;
         }
 
         mFromToPipe = new SocketPipeThread(mFromSocketInputStream, mToSocketOutputStream);
@@ -170,4 +169,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
index b361a89..ce22fa0 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
@@ -61,7 +61,6 @@
     public void run() {
         while (true) {
             Socket localSocket;
-            Socket remoteSocket;
             try {
                 localSocket = mServerSocket.accept();
             } catch (IOException e) {
@@ -70,24 +69,28 @@
                 break;
             }
 
-            remoteSocket = AdbUtils.createSocket();
-
-            if (remoteSocket == null) {
+            Socket remoteSocket = null;
+            final ConnectionHandler connectionHandler;
+            try {
+                remoteSocket = AdbUtils.createSocket();
+                connectionHandler = new ConnectionHandler(
+                        mRemoteMachineIpAddress, mPort, localSocket, remoteSocket);
+            } catch (IOException exception) {
                 try {
                     localSocket.close();
                 } catch (IOException e) {
                     Log.e(LOG_TAG, "mPort=" + mPort, e);
                 }
-
-                Log.e(LOG_TAG, "run(): mPort= " + mPort + " Failed to start forwarding from " +
-                        localSocket);
+                if (remoteSocket != null) {
+                    try {
+                        remoteSocket.close();
+                    } catch (IOException e) {
+                        Log.e(LOG_TAG, "mPort=" + mPort, e);
+                    }
+                }
                 continue;
             }
 
-            final ConnectionHandler connectionHandler =
-                    new ConnectionHandler(mRemoteMachineIpAddress, mPort, localSocket,
-                            remoteSocket);
-
             /**
              * We have to close the sockets after the ConnectionHandler finishes, so we
              * don't get "Too may open files" exception. We also remove the ConnectionHandler
@@ -126,4 +129,4 @@
             Log.e(LOG_TAG, "mPort=" + mPort, e);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
index 35de88a..5de69a7 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -45,6 +45,7 @@
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -69,6 +70,9 @@
     private static final int MSG_LOADED_ITEMS = 0;
     private static final int MSG_SHOW_PROGRESS_DIALOG = 1;
 
+    private static final CharSequence NO_RESPONSE_MESSAGE =
+            "No response from host when getting directory contents. Is the host server running?";
+
     /** Initialized lazily before first sProgressDialog.show() */
     private static ProgressDialog sProgressDialog;
 
@@ -349,13 +353,18 @@
             @Override
             public void handleMessage(Message msg) {
                 if (msg.what == MSG_LOADED_ITEMS) {
-                    setListAdapter(new DirListAdapter(DirListActivity.this,
-                            (ListItem[])msg.obj));
-                    delayedDialogHandler.removeMessages(MSG_SHOW_PROGRESS_DIALOG);
                     setTitle(shortenTitle(mCurrentDirPath));
+                    delayedDialogHandler.removeMessages(MSG_SHOW_PROGRESS_DIALOG);
                     if (sProgressDialog != null) {
                         sProgressDialog.dismiss();
                     }
+                    if (msg.obj == null) {
+                        Toast.makeText(DirListActivity.this, NO_RESPONSE_MESSAGE,
+                                Toast.LENGTH_LONG).show();
+                    } else {
+                        setListAdapter(new DirListAdapter(DirListActivity.this,
+                                (ListItem[])msg.obj));
+                    }
                 }
             }
         }).start();
@@ -389,15 +398,21 @@
         List<ListItem> subDirs = new ArrayList<ListItem>();
         List<ListItem> subFiles = new ArrayList<ListItem>();
 
-        for (String dirRelativePath : FsUtils.getLayoutTestsDirContents(dirPath, false,
-                true)) {
+        List<String> dirRelativePaths = FsUtils.getLayoutTestsDirContents(dirPath, false, true);
+        if (dirRelativePaths == null) {
+            return null;
+        }
+        for (String dirRelativePath : dirRelativePaths) {
             if (FileFilter.isTestDir(new File(dirRelativePath).getName())) {
                 subDirs.add(new ListItem(dirRelativePath, true));
             }
         }
 
-        for (String testRelativePath : FsUtils.getLayoutTestsDirContents(dirPath, false,
-                false)) {
+        List<String> testRelativePaths = FsUtils.getLayoutTestsDirContents(dirPath, false, false);
+        if (testRelativePaths == null) {
+            return null;
+        }
+        for (String testRelativePath : testRelativePaths) {
             if (FileFilter.isTestFile(new File(testRelativePath).getName())) {
                 subFiles.add(new ListItem(testRelativePath, false));
             }
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index e31711e..7e69ee4 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 (FLAG_FULLSCREEN)") {
             public void run() {
                 Window win = getWindow();
@@ -147,6 +168,24 @@
                     }, 3000);
             }
         },
+        new Test("Disable Navigation") {
+            public void run() {
+                mStatusBarManager.disable(StatusBarManager.DISABLE_NAVIGATION);
+            }
+        },
+        new Test("Disable everything in 3 sec") {
+            public void run() {
+                mHandler.postDelayed(new Runnable() {
+                        public void run() {
+                            mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND
+                                    | StatusBarManager.DISABLE_NOTIFICATION_ICONS
+                                    | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+                                    | StatusBarManager.DISABLE_SYSTEM_INFO
+                                    | StatusBarManager.DISABLE_NAVIGATION);
+                        }
+                    }, 3000);
+            }
+        },
         new Test("Enable everything") {
             public void run() {
                 mStatusBarManager.disable(0);
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index 135a633..b27ce0e 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -25,15 +25,11 @@
 # We need to process the framework classes.jar file, but we can't
 # depend directly on it (private vars won't be inherited correctly).
 # So, we depend on framework's BUILT file.
-built_framework_dep := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/javalib.jar
-built_framework_classes := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/classes.jar
+built_framework_dep := $(call java-lib-deps,framework)
+built_framework_classes := $(call java-lib-files,framework)
 
-built_core_dep := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/javalib.jar
-built_core_classes := \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/classes.jar
+built_core_dep := $(call java-lib-deps,core)
+built_core_classes := $(call java-lib-files,core)
 
 built_layoutlib_create_jar := $(call intermediates-dir-for, \
 			JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar
diff --git a/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/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
index e0dc55f..adb693d 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
@@ -17,6 +17,8 @@
 package com.android.layoutlib.bridge;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
 
 import junit.framework.TestCase;
 
@@ -26,7 +28,8 @@
         // TODO: we want to test all the classes. For now only Paint passes the tests.
 //        final String[] classes = CreateInfo.RENAMED_CLASSES;
         final String[] classes = new String[] {
-                "android.graphics.Paint",               "android.graphics._Original_Paint"
+                "android.graphics.Paint",               "android.graphics._Original_Paint",
+                "android.graphics.Canvas",               "android.graphics._Original_Canvas",
         };
         final int count = classes.length;
         for (int i = 0 ; i < count ; i += 2) {
@@ -52,12 +55,21 @@
         Method[] oldClassMethods = oldClass.getDeclaredMethods();
 
         for (Method oldMethod : oldClassMethods) {
-            // we ignore anything that starts with native
+            // we ignore anything that starts with native. This is because the class we are looking
+            // at has already been modified to remove the native modifiers.
             if (oldMethod.getName().startsWith("native")) {
                 continue;
             }
+
+            // or static and private
+            int privateStatic = Modifier.STATIC | Modifier.PRIVATE;
+            if ((oldMethod.getModifiers() & privateStatic) == privateStatic) {
+                continue;
+            }
+
             boolean found = false;
             for (Method newMethod : newClassMethods) {
+
                 if (compareMethods(newClass, newMethod, oldClass, oldMethod)) {
                     found = true;
                     break;
@@ -65,7 +77,31 @@
             }
 
             if (found == false) {
-                fail(String.format("Unable to find %1$s", oldMethod.toGenericString()));
+                // compute a full class name that's long but not too long.
+                StringBuilder sb = new StringBuilder(oldMethod.getName() + "(");
+                Type[] params = oldMethod.getGenericParameterTypes();
+                for (int j = 0; j < params.length; j++) {
+                    if (params[j] instanceof Class) {
+                        Class theClass = (Class)params[j];
+                        sb.append(theClass.getName());
+                        int dimensions = 0;
+                        while (theClass.isArray()) {
+                            dimensions++;
+                            theClass = theClass.getComponentType();
+                        }
+                        for (int i = 0; i < dimensions; i++) {
+                            sb.append("[]");
+                        }
+
+                    } else {
+                        sb.append(params[j].toString());
+                    }
+                if (j < (params.length - 1))
+                    sb.append(",");
+                }
+                sb.append(")");
+
+                fail(String.format("Missing %1$s.%2$s", newClass.getName(), sb.toString()));
             }
         }
 
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index 4851a46..dfa6841 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -81,7 +81,7 @@
     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 static final AudioCodec[] sCodecs = {GSM, PCMU, PCMA};
 
     private AudioCodec(int type, String rtpmap, String fmtp) {
         this.type = type;
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 2135fcb..7c1234a 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -46,7 +46,7 @@
  * Class that handles an audio call over SIP.
  */
 /** @hide */
-public class SipAudioCall extends SipSessionAdapter {
+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;
@@ -530,7 +530,7 @@
      * 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 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
@@ -538,16 +538,12 @@
      *        call
      */
     public synchronized void makeCall(SipProfile peerProfile,
-        SipManager sipManager, int timeout) throws SipException {
-        SipSession s = mSipSession = sipManager.createSipSession(
-                mLocalProfile, createListener());
-        if (s == null) {
-            throw new SipException(
-                    "Failed to create SipSession; network available?");
-        }
+            SipSession sipSession, int timeout) throws SipException {
+        mSipSession = sipSession;
         try {
             mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
-            s.makeCall(peerProfile, createOffer().encode(), timeout);
+            sipSession.setListener(createListener());
+            sipSession.makeCall(peerProfile, createOffer().encode(), timeout);
         } catch (IOException e) {
             throw new SipException("makeCall()", e);
         }
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 5976a04..59631c1 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -269,7 +269,12 @@
             throws SipException {
         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;
     }
 
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 98%
rename from services/java/com/android/server/sip/SipService.java
rename to voip/java/com/android/server/sip/SipService.java
index 3f43e1c..0ff5586 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -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) {
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
similarity index 94%
rename from services/java/com/android/server/sip/SipSessionGroup.java
rename to voip/java/com/android/server/sip/SipSessionGroup.java
index fa3f64a..4321d7b 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -28,7 +28,6 @@
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipProfile;
 import android.net.sip.SipSession;
-import android.net.sip.SipSessionAdapter;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -82,7 +81,6 @@
     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 = 3; // in seconds
 
@@ -620,13 +618,15 @@
                         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;
                 }
-                return true;
             } else if (isRequestEvent(Request.OPTIONS, evt)) {
                 mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
                 return true;
@@ -646,6 +646,37 @@
             }
         }
 
+        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) {
@@ -661,19 +692,7 @@
         }
 
         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 != SipSession.State.PINGING)) {
-                Log.d(TAG, "not the current transaction; current=" + current
-                        + ", timed out=" + target);
-                return;
-            }
+            Log.d(TAG, "processing Timeout...");
             switch (mState) {
                 case SipSession.State.REGISTERING:
                 case SipSession.State.DEREGISTERING:
@@ -810,6 +829,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
@@ -831,6 +856,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);
@@ -924,6 +958,11 @@
                 int statusCode = response.getStatusCode();
                 switch (statusCode) {
                 case Response.RINGING:
+                case Response.CALL_IS_BEING_FORWARDED:
+                case Response.QUEUED:
+                case Response.SESSION_PROGRESS:
+                    // feedback any provisional responses (except TRYING) as
+                    // ring back for better UX
                     if (mState == SipSession.State.OUTGOING_CALL) {
                         mState = SipSession.State.OUTGOING_CALL_RING_BACK;
                         mProxy.onRingingBack(this);
@@ -937,7 +976,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,
@@ -982,19 +1024,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;
                 }
@@ -1060,8 +1102,8 @@
         }
 
         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() {
@@ -1165,8 +1207,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;
             }
diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/voip/java/com/android/server/sip/SipSessionListenerProxy.java
similarity index 100%
rename from services/java/com/android/server/sip/SipSessionListenerProxy.java
rename to voip/java/com/android/server/sip/SipSessionListenerProxy.java
diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk
index a364355..29683bd 100644
--- a/voip/jni/rtp/Android.mk
+++ b/voip/jni/rtp/Android.mk
@@ -26,16 +26,21 @@
 	util.cpp \
 	rtp_jni.cpp
 
+LOCAL_SRC_FILES += \
+	G711Codec.cpp \
+	GsmCodec.cpp
+
 LOCAL_SHARED_LIBRARIES := \
 	libnativehelper \
 	libcutils \
 	libutils \
 	libmedia
 
-LOCAL_STATIC_LIBRARIES :=
+LOCAL_STATIC_LIBRARIES := libgsm
 
 LOCAL_C_INCLUDES += \
-	$(JNI_H_INCLUDE)
+	$(JNI_H_INCLUDE) \
+	external/libgsm/inc
 
 LOCAL_CFLAGS += -fvisibility=hidden
 
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
index 4d8d36c..fc33ef2 100644
--- a/voip/jni/rtp/AudioCodec.cpp
+++ b/voip/jni/rtp/AudioCodec.cpp
@@ -18,124 +18,9 @@
 
 #include "AudioCodec.h"
 
-namespace {
-
-int8_t gExponents[128] = {
-    0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
-    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-};
-
-//------------------------------------------------------------------------------
-
-class UlawCodec : public AudioCodec
-{
-public:
-    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);
-private:
-    int mSampleCount;
-};
-
-int UlawCodec::encode(void *payload, int16_t *samples)
-{
-    int8_t *ulaws = (int8_t *)payload;
-    for (int i = 0; i < mSampleCount; ++i) {
-        int sample = samples[i];
-        int sign = (sample >> 8) & 0x80;
-        if (sample < 0) {
-            sample = -sample;
-        }
-        sample += 132;
-        if (sample > 32767) {
-            sample = 32767;
-        }
-        int exponent = gExponents[sample >> 8];
-        int mantissa = (sample >> (exponent + 3)) & 0x0F;
-        ulaws[i] = ~(sign | (exponent << 4) | mantissa);
-    }
-    return mSampleCount;
-}
-
-int UlawCodec::decode(int16_t *samples, void *payload, int length)
-{
-    int8_t *ulaws = (int8_t *)payload;
-    for (int i = 0; i < length; ++i) {
-        int ulaw = ~ulaws[i];
-        int exponent = (ulaw >> 4) & 0x07;
-        int mantissa = ulaw & 0x0F;
-        int sample = (((mantissa << 3) + 132) << exponent) - 132;
-        samples[i] = (ulaw < 0 ? -sample : sample);
-    }
-    return length;
-}
-
-AudioCodec *newUlawCodec()
-{
-    return new UlawCodec;
-}
-
-//------------------------------------------------------------------------------
-
-class AlawCodec : public AudioCodec
-{
-public:
-    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);
-private:
-    int mSampleCount;
-};
-
-int AlawCodec::encode(void *payload, int16_t *samples)
-{
-    int8_t *alaws = (int8_t *)payload;
-    for (int i = 0; i < mSampleCount; ++i) {
-        int sample = samples[i];
-        int sign = (sample >> 8) & 0x80;
-        if (sample < 0) {
-            sample = -sample;
-        }
-        if (sample > 32767) {
-            sample = 32767;
-        }
-        int exponent = gExponents[sample >> 8];
-        int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F;
-        alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5;
-    }
-    return mSampleCount;
-}
-
-int AlawCodec::decode(int16_t *samples, void *payload, int length)
-{
-    int8_t *alaws = (int8_t *)payload;
-    for (int i = 0; i < length; ++i) {
-        int alaw = alaws[i] ^ 0x55;
-        int exponent = (alaw >> 4) & 0x07;
-        int mantissa = alaw & 0x0F;
-        int sample = (exponent == 0 ? (mantissa << 4) + 8 :
-            ((mantissa << 3) + 132) << exponent);
-        samples[i] = (alaw < 0 ? sample : -sample);
-    }
-    return length;
-}
-
-AudioCodec *newAlawCodec()
-{
-    return new AlawCodec;
-}
+extern AudioCodec *newAlawCodec();
+extern AudioCodec *newUlawCodec();
+extern AudioCodec *newGsmCodec();
 
 struct AudioCodecType {
     const char *name;
@@ -143,11 +28,10 @@
 } gAudioCodecTypes[] = {
     {"PCMA", newAlawCodec},
     {"PCMU", newUlawCodec},
+    {"GSM", newGsmCodec},
     {NULL, NULL},
 };
 
-} // namespace
-
 AudioCodec *newAudioCodec(const char *codecName)
 {
     AudioCodecType *type = gAudioCodecTypes;
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 7cf06137..72c882b 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -86,6 +86,8 @@
     void decode(int tick);
 
 private:
+    bool isNatAddress(struct sockaddr_storage *addr);
+
     enum {
         NORMAL = 0,
         SEND_ONLY = 1,
@@ -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?
@@ -436,18 +461,15 @@
         EC_ENABLED = 3,
         LAST_MODE = 3,
     };
-    int mMode;
+
     AudioStream *mChain;
     int mEventQueue;
     volatile int mDtmfEvent;
 
+    int mMode;
+    int mSampleRate;
     int mSampleCount;
     int mDeviceSocket;
-    AudioTrack mTrack;
-    AudioRecord mRecord;
-
-    bool networkLoop();
-    bool deviceLoop();
 
     class NetworkThread : public Thread
     {
@@ -465,10 +487,7 @@
 
     private:
         AudioGroup *mGroup;
-        bool threadLoop()
-        {
-            return mGroup->networkLoop();
-        }
+        bool threadLoop();
     };
     sp<NetworkThread> mNetworkThread;
 
@@ -479,9 +498,6 @@
 
         bool start()
         {
-            char c;
-            while (recv(mGroup->mDeviceSocket, &c, 1, MSG_DONTWAIT) == 1);
-
             if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
                 LOGE("cannot start device thread");
                 return false;
@@ -491,10 +507,7 @@
 
     private:
         AudioGroup *mGroup;
-        bool threadLoop()
-        {
-            return mGroup->deviceLoop();
-        }
+        bool threadLoop();
     };
     sp<DeviceThread> mDeviceThread;
 };
@@ -514,8 +527,6 @@
 {
     mNetworkThread->requestExitAndWait();
     mDeviceThread->requestExitAndWait();
-    mTrack.stop();
-    mRecord.stop();
     close(mEventQueue);
     close(mDeviceSocket);
     while (mChain) {
@@ -534,40 +545,9 @@
         return false;
     }
 
+    mSampleRate = sampleRate;
     mSampleCount = sampleCount;
 
-    // Find out the frame count for AudioTrack and AudioRecord.
-    int output = 0;
-    int input = 0;
-    if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
-        sampleRate) != NO_ERROR || output <= 0 ||
-        AudioRecord::getMinFrameCount(&input, sampleRate,
-        AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
-        LOGE("cannot compute frame count");
-        return false;
-    }
-    LOGD("reported frame count: output %d, input %d", output, input);
-
-    if (output < sampleCount * 2) {
-        output = sampleCount * 2;
-    }
-    if (input < sampleCount * 2) {
-        input = sampleCount * 2;
-    }
-    LOGD("adjusted frame count: output %d, input %d", output, input);
-
-    // Initialize AudioTrack and AudioRecord.
-    if (mTrack.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT,
-        AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR ||
-        mRecord.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT,
-        AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) {
-        LOGE("cannot initialize audio device");
-        return false;
-    }
-    LOGD("latency: output %d, input %d", mTrack.latency(), mRecord.latency());
-
-    // TODO: initialize echo canceler here.
-
     // Create device socket.
     int pair[2];
     if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
@@ -585,13 +565,11 @@
         return false;
     }
 
-    // Give device socket a reasonable timeout and buffer size.
+    // Give device socket a reasonable timeout.
     timeval tv;
     tv.tv_sec = 0;
     tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
-    if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) ||
-        setsockopt(pair[0], SOL_SOCKET, SO_RCVBUF, &output, sizeof(output)) ||
-        setsockopt(pair[1], SOL_SOCKET, SO_SNDBUF, &output, sizeof(output))) {
+    if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
         LOGE("setsockopt: %s", strerror(errno));
         return false;
     }
@@ -619,29 +597,10 @@
         return true;
     }
 
+    mDeviceThread->requestExitAndWait();
     LOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
     mMode = mode;
-
-    mDeviceThread->requestExitAndWait();
-    if (mode == ON_HOLD) {
-        mTrack.stop();
-        mRecord.stop();
-        return true;
-    }
-
-    mTrack.start();
-    if (mode == MUTED) {
-        mRecord.stop();
-    } else {
-        mRecord.start();
-    }
-
-    if (!mDeviceThread->start()) {
-        mTrack.stop();
-        mRecord.stop();
-        return false;
-    }
-    return true;
+    return (mode == ON_HOLD) || mDeviceThread->start();
 }
 
 bool AudioGroup::sendDtmf(int event)
@@ -716,15 +675,16 @@
     return true;
 }
 
-bool AudioGroup::networkLoop()
+bool AudioGroup::NetworkThread::threadLoop()
 {
+    AudioStream *chain = mGroup->mChain;
     int tick = elapsedRealtime();
     int deadline = tick + 10;
     int count = 0;
 
-    for (AudioStream *stream = mChain; stream; stream = stream->mNext) {
+    for (AudioStream *stream = chain; stream; stream = stream->mNext) {
         if (!stream->mTick || tick - stream->mTick >= 0) {
-            stream->encode(tick, mChain);
+            stream->encode(tick, chain);
         }
         if (deadline - stream->mTick > 0) {
             deadline = stream->mTick;
@@ -732,12 +692,12 @@
         ++count;
     }
 
-    if (mDtmfEvent != -1) {
-        int event = mDtmfEvent;
-        for (AudioStream *stream = mChain; stream; stream = stream->mNext) {
+    int event = mGroup->mDtmfEvent;
+    if (event != -1) {
+        for (AudioStream *stream = chain; stream; stream = stream->mNext) {
             stream->sendDtmf(event);
         }
-        mDtmfEvent = -1;
+        mGroup->mDtmfEvent = -1;
     }
 
     deadline -= tick;
@@ -746,7 +706,7 @@
     }
 
     epoll_event events[count];
-    count = epoll_wait(mEventQueue, events, count, deadline);
+    count = epoll_wait(mGroup->mEventQueue, events, count, deadline);
     if (count == -1) {
         LOGE("epoll_wait: %s", strerror(errno));
         return false;
@@ -758,70 +718,125 @@
     return true;
 }
 
-bool AudioGroup::deviceLoop()
+bool AudioGroup::DeviceThread::threadLoop()
 {
-    int16_t output[mSampleCount];
+    int mode = mGroup->mMode;
+    int sampleRate = mGroup->mSampleRate;
+    int sampleCount = mGroup->mSampleCount;
+    int deviceSocket = mGroup->mDeviceSocket;
 
-    if (recv(mDeviceSocket, output, sizeof(output), 0) <= 0) {
-        memset(output, 0, sizeof(output));
-    }
-
-    int16_t input[mSampleCount];
-    int toWrite = mSampleCount;
-    int toRead = (mMode == MUTED) ? 0 : mSampleCount;
-    int chances = 100;
-
-    while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
-        if (toWrite > 0) {
-            AudioTrack::Buffer buffer;
-            buffer.frameCount = toWrite;
-
-            status_t status = mTrack.obtainBuffer(&buffer, 1);
-            if (status == NO_ERROR) {
-                memcpy(buffer.i8, &output[mSampleCount - toWrite], buffer.size);
-                toWrite -= buffer.frameCount;
-                mTrack.releaseBuffer(&buffer);
-            } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
-                LOGE("cannot write to AudioTrack");
-                return false;
-            }
-        }
-
-        if (toRead > 0) {
-            AudioRecord::Buffer buffer;
-            buffer.frameCount = mRecord.frameCount();
-
-            status_t status = mRecord.obtainBuffer(&buffer, 1);
-            if (status == NO_ERROR) {
-                int count = ((int)buffer.frameCount < toRead) ?
-                        buffer.frameCount : toRead;
-                memcpy(&input[mSampleCount - toRead], buffer.i8, count * 2);
-                toRead -= count;
-                if (buffer.frameCount < mRecord.frameCount()) {
-                    buffer.frameCount = count;
-                }
-                mRecord.releaseBuffer(&buffer);
-            } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
-                LOGE("cannot read from AudioRecord");
-                return false;
-            }
-        }
-    }
-
-    if (!chances) {
-        LOGE("device loop timeout");
+    // Find out the frame count for AudioTrack and AudioRecord.
+    int output = 0;
+    int input = 0;
+    if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
+        sampleRate) != NO_ERROR || output <= 0 ||
+        AudioRecord::getMinFrameCount(&input, sampleRate,
+        AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
+        LOGE("cannot compute frame count");
         return false;
     }
+    LOGD("reported frame count: output %d, input %d", output, input);
 
-    if (mMode != MUTED) {
-        if (mMode == NORMAL) {
-            send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
-        } else {
-            // TODO: Echo canceller runs here.
-            send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
+    if (output < sampleCount * 2) {
+        output = sampleCount * 2;
+    }
+    if (input < sampleCount * 2) {
+        input = sampleCount * 2;
+    }
+    LOGD("adjusted frame count: output %d, input %d", output, input);
+
+    // Initialize AudioTrack and AudioRecord.
+    AudioTrack track;
+    AudioRecord record;
+    if (track.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT,
+        AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR ||
+        record.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT,
+        AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) {
+        LOGE("cannot initialize audio device");
+        return false;
+    }
+    LOGD("latency: output %d, input %d", track.latency(), record.latency());
+
+    // TODO: initialize echo canceler here.
+
+    // Give device socket a reasonable buffer size.
+    setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
+    setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output));
+
+    // Drain device socket.
+    char c;
+    while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
+
+    // Start your engine!
+    track.start();
+    if (mode != MUTED) {
+        record.start();
+    }
+
+    while (!exitPending()) {
+        int16_t output[sampleCount];
+        if (recv(deviceSocket, output, sizeof(output), 0) <= 0) {
+            memset(output, 0, sizeof(output));
+        }
+
+        int16_t input[sampleCount];
+        int toWrite = sampleCount;
+        int toRead = (mode == MUTED) ? 0 : sampleCount;
+        int chances = 100;
+
+        while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
+            if (toWrite > 0) {
+                AudioTrack::Buffer buffer;
+                buffer.frameCount = toWrite;
+
+                status_t status = track.obtainBuffer(&buffer, 1);
+                if (status == NO_ERROR) {
+                    int offset = sampleCount - toWrite;
+                    memcpy(buffer.i8, &output[offset], buffer.size);
+                    toWrite -= buffer.frameCount;
+                    track.releaseBuffer(&buffer);
+                } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
+                    LOGE("cannot write to AudioTrack");
+                    break;
+                }
+            }
+
+            if (toRead > 0) {
+                AudioRecord::Buffer buffer;
+                buffer.frameCount = record.frameCount();
+
+                status_t status = record.obtainBuffer(&buffer, 1);
+                if (status == NO_ERROR) {
+                    int count = ((int)buffer.frameCount < toRead) ?
+                            buffer.frameCount : toRead;
+                    memcpy(&input[sampleCount - toRead], buffer.i8, count * 2);
+                    toRead -= count;
+                    if (buffer.frameCount < record.frameCount()) {
+                        buffer.frameCount = count;
+                    }
+                    record.releaseBuffer(&buffer);
+                } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
+                    LOGE("cannot read from AudioRecord");
+                    break;
+                }
+            }
+        }
+
+        if (chances <= 0) {
+            LOGE("device loop timeout");
+            break;
+        }
+
+        if (mode != MUTED) {
+            if (mode == NORMAL) {
+                send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
+            } else {
+                // TODO: Echo canceller runs here.
+                send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
+            }
         }
     }
-    return true;
+    return false;
 }
 
 //------------------------------------------------------------------------------
diff --git a/voip/jni/rtp/G711Codec.cpp b/voip/jni/rtp/G711Codec.cpp
new file mode 100644
index 0000000..091afa9
--- /dev/null
+++ b/voip/jni/rtp/G711Codec.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyrightm (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 "AudioCodec.h"
+
+namespace {
+
+int8_t gExponents[128] = {
+    0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+};
+
+//------------------------------------------------------------------------------
+
+class UlawCodec : public AudioCodec
+{
+public:
+    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);
+private:
+    int mSampleCount;
+};
+
+int UlawCodec::encode(void *payload, int16_t *samples)
+{
+    int8_t *ulaws = (int8_t *)payload;
+    for (int i = 0; i < mSampleCount; ++i) {
+        int sample = samples[i];
+        int sign = (sample >> 8) & 0x80;
+        if (sample < 0) {
+            sample = -sample;
+        }
+        sample += 132;
+        if (sample > 32767) {
+            sample = 32767;
+        }
+        int exponent = gExponents[sample >> 8];
+        int mantissa = (sample >> (exponent + 3)) & 0x0F;
+        ulaws[i] = ~(sign | (exponent << 4) | mantissa);
+    }
+    return mSampleCount;
+}
+
+int UlawCodec::decode(int16_t *samples, void *payload, int length)
+{
+    int8_t *ulaws = (int8_t *)payload;
+    for (int i = 0; i < length; ++i) {
+        int ulaw = ~ulaws[i];
+        int exponent = (ulaw >> 4) & 0x07;
+        int mantissa = ulaw & 0x0F;
+        int sample = (((mantissa << 3) + 132) << exponent) - 132;
+        samples[i] = (ulaw < 0 ? -sample : sample);
+    }
+    return length;
+}
+
+//------------------------------------------------------------------------------
+
+class AlawCodec : public AudioCodec
+{
+public:
+    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);
+private:
+    int mSampleCount;
+};
+
+int AlawCodec::encode(void *payload, int16_t *samples)
+{
+    int8_t *alaws = (int8_t *)payload;
+    for (int i = 0; i < mSampleCount; ++i) {
+        int sample = samples[i];
+        int sign = (sample >> 8) & 0x80;
+        if (sample < 0) {
+            sample = -sample;
+        }
+        if (sample > 32767) {
+            sample = 32767;
+        }
+        int exponent = gExponents[sample >> 8];
+        int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F;
+        alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5;
+    }
+    return mSampleCount;
+}
+
+int AlawCodec::decode(int16_t *samples, void *payload, int length)
+{
+    int8_t *alaws = (int8_t *)payload;
+    for (int i = 0; i < length; ++i) {
+        int alaw = alaws[i] ^ 0x55;
+        int exponent = (alaw >> 4) & 0x07;
+        int mantissa = alaw & 0x0F;
+        int sample = (exponent == 0 ? (mantissa << 4) + 8 :
+            ((mantissa << 3) + 132) << exponent);
+        samples[i] = (alaw < 0 ? sample : -sample);
+    }
+    return length;
+}
+
+} // namespace
+
+AudioCodec *newUlawCodec()
+{
+    return new UlawCodec;
+}
+
+AudioCodec *newAlawCodec()
+{
+    return new AlawCodec;
+}
diff --git a/voip/jni/rtp/GsmCodec.cpp b/voip/jni/rtp/GsmCodec.cpp
new file mode 100644
index 0000000..8d2286e
--- /dev/null
+++ b/voip/jni/rtp/GsmCodec.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyrightm (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 "AudioCodec.h"
+
+extern "C" {
+#include "gsm.h"
+}
+
+namespace {
+
+class GsmCodec : public AudioCodec
+{
+public:
+    GsmCodec() {
+        mEncode = gsm_create();
+        mDecode = gsm_create();
+    }
+
+    ~GsmCodec() {
+        if (mEncode) {
+            gsm_destroy(mEncode);
+        }
+        if (mDecode) {
+            gsm_destroy(mDecode);
+        }
+    }
+
+    int set(int sampleRate, const char *fmtp) {
+        return (sampleRate == 8000 && mEncode && mDecode) ? 160 : -1;
+    }
+
+    int encode(void *payload, int16_t *samples);
+    int decode(int16_t *samples, void *payload, int length);
+
+private:
+    gsm mEncode;
+    gsm mDecode;
+};
+
+int GsmCodec::encode(void *payload, int16_t *samples)
+{
+    gsm_encode(mEncode, samples, (unsigned char *)payload);
+    return 33;
+}
+
+int GsmCodec::decode(int16_t *samples, void *payload, int length)
+{
+    if (length == 33 &&
+        gsm_decode(mDecode, (unsigned char *)payload, samples) == 0) {
+        return 160;
+    }
+    return -1;
+}
+
+} // namespace
+
+AudioCodec *newGsmCodec()
+{
+    return new GsmCodec;
+}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 7e26028..e82c003 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -63,9 +63,11 @@
 import android.util.Log;
 import android.util.Slog;
 import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothProfile;
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.Context;
@@ -444,8 +446,14 @@
         mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
         mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
 
-        mBluetoothHeadset = new BluetoothHeadset(mContext, null);
         mLinkProperties = new LinkProperties();
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.A2DP);
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
 
         mNetworkInfo.setIsAvailable(false);
         mLinkProperties.clear();
@@ -1278,24 +1286,50 @@
      *
      * @return Whether to disable coexistence mode.
      */
-    private boolean shouldDisableCoexistenceMode() {
-        int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
-        return state == BluetoothHeadset.STATE_DISCONNECTED;
+    private synchronized boolean shouldDisableCoexistenceMode() {
+        Set<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
+
+        return (devices.size() != 0 ? true : false);
     }
 
-    private void checkIsBluetoothPlaying() {
+    private synchronized void checkIsBluetoothPlaying() {
         boolean isBluetoothPlaying = false;
-        Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
+        if (mBluetoothA2dp != null) {
+            Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedDevices();
 
-        for (BluetoothDevice device : connected) {
-            if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
-                isBluetoothPlaying = true;
-                break;
+            for (BluetoothDevice device : connected) {
+                if (mBluetoothA2dp.isA2dpPlaying(device)) {
+                    isBluetoothPlaying = true;
+                    break;
+                }
             }
         }
         setBluetoothScanMode(isBluetoothPlaying);
     }
 
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            synchronized (WifiStateMachine.this) {
+                if (profile == BluetoothProfile.HEADSET) {
+                    mBluetoothHeadset = (BluetoothHeadset) proxy;
+                } else if (profile == BluetoothProfile.A2DP) {
+                    mBluetoothA2dp = (BluetoothA2dp)proxy;
+                }
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            synchronized (WifiStateMachine.this) {
+                if (profile == BluetoothProfile.HEADSET) {
+                    mBluetoothHeadset = null;
+                } else if (profile == BluetoothProfile.A2DP) {
+                    mBluetoothA2dp = null;
+                }
+            }
+        }
+    };
+
     private void sendScanResultsAvailableBroadcast() {
         if (!ActivityManagerNative.isSystemReady()) return;
 
@@ -1905,9 +1939,6 @@
                     //TODO: initialize and fix multicast filtering
                     //mWM.initializeMulticastFiltering();
 
-                    if (mBluetoothA2dp == null) {
-                        mBluetoothA2dp = new BluetoothA2dp(mContext);
-                    }
                     checkIsBluetoothPlaying();
 
                     sendSupplicantConnectionChangedBroadcast(true);
@@ -2458,7 +2489,10 @@
                 mModifiedBluetoothCoexistenceMode = false;
                 mPowerMode = DRIVER_POWER_MODE_AUTO;
 
-                if (shouldDisableCoexistenceMode()) {
+                // TODO(): Incorporate the else part in the state machine
+                // If mBluetoothHeadset == null, means it not conencted to the
+                // service yet.
+                if (mBluetoothHeadset != null && shouldDisableCoexistenceMode()) {
                     /*
                      * There are problems setting the Wi-Fi driver's power
                      * mode to active when bluetooth coexistence mode is