resolved conflicts for merge of faf083ef to master

Change-Id: I316a1b4356f6dba6c3880ccb02dbb2fa00d21a85
diff --git a/Android.mk b/Android.mk
index 7728b02..41b7809 100644
--- a/Android.mk
+++ b/Android.mk
@@ -115,6 +115,7 @@
 	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
 	core/java/android/database/IContentObserver.aidl \
+	core/java/android/hardware/IUsbManager.aidl \
 	core/java/android/net/IConnectivityManager.aidl \
 	core/java/android/net/INetworkManagementEventObserver.aidl \
 	core/java/android/net/IThrottleManager.aidl \
diff --git a/api/current.xml b/api/current.xml
index 42a7ae1..50a4fcf 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -111,6 +111,17 @@
  visibility="public"
 >
 </field>
+<field name="ACCESS_USB"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.ACCESS_USB&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACCESS_WIFI_STATE"
  type="java.lang.String"
  transient="false"
@@ -22636,6 +22647,19 @@
 <parameter name="id" type="int">
 </parameter>
 </method>
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ev" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="dispatchKeyEvent"
  return="boolean"
  abstract="false"
@@ -23466,6 +23490,19 @@
  visibility="public"
 >
 </method>
+<method name="onGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="onKeyDown"
  return="boolean"
  abstract="false"
@@ -27407,6 +27444,19 @@
  visibility="public"
 >
 </method>
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ev" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="dispatchKeyEvent"
  return="boolean"
  abstract="false"
@@ -27762,6 +27812,19 @@
  visibility="public"
 >
 </method>
+<method name="onGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="onKeyDown"
  return="boolean"
  abstract="false"
@@ -28645,6 +28708,29 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="completedDownload"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.String">
+</parameter>
+<parameter name="description" type="java.lang.String">
+</parameter>
+<parameter name="isMediaScannerScannable" type="boolean">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="length" type="long">
+</parameter>
+</method>
 <method name="enqueue"
  return="long"
  abstract="false"
@@ -49042,6 +49128,17 @@
  visibility="public"
 >
 </field>
+<field name="USB_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;usb&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="VIBRATOR_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -93846,6 +93943,1275 @@
 >
 </field>
 </class>
+<class name="UsbConstants"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="UsbConstants"
+ type="android.hardware.UsbConstants"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="USB_CLASS_APP_SPEC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="254"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_AUDIO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_CDC_DATA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_COMM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_CONTENT_SEC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="13"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_CSCID"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_HID"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_HUB"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_MASS_STORAGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_MISC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="239"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_PER_INTERFACE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_PHYSICA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_PRINTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_STILL_IMAGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_VENDOR_SPEC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="255"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_VIDEO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="14"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CLASS_WIRELESS_CONTROLLER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="224"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_DIR_IN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_DIR_OUT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_ENDPOINT_DIR_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_ENDPOINT_NUMBER_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="15"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_ENDPOINT_XFERTYPE_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_ENDPOINT_XFER_BULK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_ENDPOINT_XFER_CONTROL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_ENDPOINT_XFER_INT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_ENDPOINT_XFER_ISOC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_SUBCLASS_VENDOR_SPEC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="255"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_TYPE_CLASS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_TYPE_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="96"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_TYPE_RESERVED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="96"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_TYPE_STANDARD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_TYPE_VENDOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="UsbDevice"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="claimInterface"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intf" type="android.hardware.UsbInterface">
+</parameter>
+<parameter name="force" type="boolean">
+</parameter>
+</method>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDeviceClass"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDeviceId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDeviceId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="getDeviceName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDeviceName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="getDeviceProtocol"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDeviceSubclass"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFileDescriptor"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterface"
+ return="android.hardware.UsbInterface"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getInterfaceCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getProductId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSerial"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getVendorId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="releaseInterface"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intf" type="android.hardware.UsbInterface">
+</parameter>
+</method>
+<method name="requestWait"
+ return="android.hardware.UsbRequest"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="sendControlRequest"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="requestType" type="int">
+</parameter>
+<parameter name="request" type="int">
+</parameter>
+<parameter name="value" type="int">
+</parameter>
+<parameter name="index" type="int">
+</parameter>
+<parameter name="buffer" type="byte[]">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="UsbEndpoint"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ 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="getAddress"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAttributes"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDevice"
+ return="android.hardware.UsbDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDirection"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEndpointNumber"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterface"
+ return="android.hardware.UsbInterface"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterval"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMaxPacketSize"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ 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="parcel" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="UsbInterface"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ 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="getDevice"
+ return="android.hardware.UsbDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEndpoint"
+ return="android.hardware.UsbEndpoint"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getEndpointCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterfaceClass"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterfaceProtocol"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterfaceSubclass"
+ 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="parcel" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="UsbManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getDeviceList"
+ return="java.util.HashMap&lt;java.lang.String, android.hardware.UsbDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFunctionEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="function" type="java.lang.String">
+</parameter>
+</method>
+<method name="isFunctionSupported"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="function" type="java.lang.String">
+</parameter>
+</method>
+<method name="openDevice"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.hardware.UsbDevice">
+</parameter>
+</method>
+<field name="ACTION_USB_DEVICE_ATTACHED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.action.USB_DEVICE_ATTACHED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_USB_DEVICE_DETACHED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.action.USB_DEVICE_DETACHED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_USB_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.hardware.action.USB_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;device&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE_CLASS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;device_class&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;device_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE_PROTOCOL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;device_protocol&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE_SUBCLASS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;device_subclass&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PRODUCT_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;product_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_VENDOR_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;vendor_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CONFIGURATION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;configuration&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_CONNECTED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;connected&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_FUNCTION_ADB"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;adb&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_FUNCTION_DISABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;disabled&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_FUNCTION_ENABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;enabled&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_FUNCTION_MASS_STORAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;mass_storage&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_FUNCTION_MTP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;mtp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USB_FUNCTION_RNDIS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;rndis&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="UsbRequest"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="UsbRequest"
+ type="android.hardware.UsbRequest"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="cancel"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClientData"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEndpoint"
+ return="android.hardware.UsbEndpoint"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="initialize"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="endpoint" type="android.hardware.UsbEndpoint">
+</parameter>
+</method>
+<method name="queue"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="buffer" type="java.nio.ByteBuffer">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setClientData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="java.lang.Object">
+</parameter>
+</method>
+</class>
 </package>
 <package name="android.inputmethodservice"
 >
@@ -194154,10 +195520,10 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
-<parameter name="addr" type="int">
+<parameter name="ipv4Address" type="int">
 </parameter>
 </method>
 <method name="formatShortFileSize"
@@ -207108,6 +208474,17 @@
  visibility="public"
 >
 </field>
+<field name="SOURCE_CLASS_JOYSTICK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SOURCE_CLASS_MASK"
  type="int"
  transient="false"
@@ -207163,6 +208540,28 @@
  visibility="public"
 >
 </field>
+<field name="SOURCE_GAMEPAD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1025"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SOURCE_JOYSTICK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16777232"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SOURCE_KEYBOARD"
  type="int"
  transient="false"
@@ -209005,6 +210404,182 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_BUTTON_1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="188"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_10"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="197"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_11"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="198"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_12"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="199"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_13"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="200"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_14"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="201"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_15"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="202"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_16"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="203"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="189"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="190"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="191"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_5"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="192"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_6"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="193"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_7"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="194"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_8"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="195"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_9"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="196"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_BUTTON_A"
  type="int"
  transient="false"
@@ -216122,6 +217697,19 @@
 <parameter name="canvas" type="android.graphics.Canvas">
 </parameter>
 </method>
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="dispatchKeyEvent"
  return="boolean"
  abstract="false"
@@ -218290,6 +219878,19 @@
 <parameter name="previouslyFocusedRect" type="android.graphics.Rect">
 </parameter>
 </method>
+<method name="onGenericMotionEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="onKeyDown"
  return="boolean"
  abstract="false"
@@ -225304,6 +226905,19 @@
 <parameter name="hardwareAccelerated" type="boolean">
 </parameter>
 </method>
+<method name="superDispatchGenericMotionEvent"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="superDispatchKeyEvent"
  return="boolean"
  abstract="true"
@@ -225649,6 +227263,19 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="dispatchGenericMotionEvent"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
 <method name="dispatchKeyEvent"
  return="boolean"
  abstract="true"
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index ba7f807..2df450f3 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -34,7 +34,6 @@
     { AID_MEDIA, "media.player" },
     { AID_MEDIA, "media.camera" },
     { AID_MEDIA, "media.audio_policy" },
-    { AID_DRMIO, "drm.drmIOService" },
     { AID_DRM,   "drm.drmManager" },
     { AID_NFC,   "nfc" },
     { AID_RADIO, "radio.phone" },
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 804aeaa..79890ef 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2109,7 +2109,39 @@
     public boolean onTrackballEvent(MotionEvent event) {
         return false;
     }
-    
+
+    /**
+     * Called when a generic motion event was not handled by any of the
+     * views inside of the activity.
+     * <p>
+     * Generic motion events are dispatched to the focused view to describe
+     * the motions of input devices such as joysticks.  The
+     * {@link MotionEvent#getSource() source} of the motion event specifies
+     * the class of input that was received.  Implementations of this method
+     * must examine the bits in the source before processing the event.
+     * The following code example shows how this is done.
+     * </p>
+     * <code>
+     * public boolean onGenericMotionEvent(MotionEvent event) {
+     *     if ((event.getSource() &amp; InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+     *         float x = event.getX();
+     *         float y = event.getY();
+     *         // process the joystick motion
+     *         return true;
+     *     }
+     *     return super.onGenericMotionEvent(event);
+     * }
+     * </code>
+     *
+     * @param event The generic motion event being processed.
+     *
+     * @return Return true if you have consumed the event, false if you haven't.
+     * The default implementation always returns false.
+     */
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        return false;
+    }
+
     /**
      * Called whenever a key, touch, or trackball event is dispatched to the
      * activity.  Implement this method if you wish to know that the user has
@@ -2292,6 +2324,24 @@
         return onTrackballEvent(ev);
     }
 
+    /**
+     * Called to process generic motion events.  You can override this to
+     * intercept all generic motion events before they are dispatched to the
+     * window.  Be sure to call this implementation for generic motion events
+     * that should be handled normally.
+     *
+     * @param ev The generic motion event.
+     *
+     * @return boolean Return true if this event was consumed.
+     */
+    public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+        onUserInteraction();
+        if (getWindow().superDispatchGenericMotionEvent(ev)) {
+            return true;
+        }
+        return onGenericMotionEvent(ev);
+    }
+
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         event.setClassName(getClass().getName());
         event.setPackageName(getPackageName());
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6f63990..8737e93 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -41,7 +41,9 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.hardware.IUsbManager;
 import android.hardware.SensorManager;
+import android.hardware.UsbManager;
 import android.location.CountryDetector;
 import android.location.ICountryDetector;
 import android.location.ILocationManager;
@@ -399,6 +401,12 @@
                     return new UiModeManager();
                 }});
 
+        registerService(USB_SERVICE, new StaticServiceFetcher() {
+                public Object createStaticService() {
+                    IBinder b = ServiceManager.getService(USB_SERVICE);
+                    return new UsbManager(IUsbManager.Stub.asInterface(b));
+                }});
+
         registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
                     return new Vibrator();
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 7365670..7b794f2 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -615,7 +615,39 @@
     public boolean onTrackballEvent(MotionEvent event) {
         return false;
     }
-    
+
+    /**
+     * Called when a generic motion event was not handled by any of the
+     * views inside of the dialog.
+     * <p>
+     * Generic motion events are dispatched to the focused view to describe
+     * the motions of input devices such as joysticks.  The
+     * {@link MotionEvent#getSource() source} of the motion event specifies
+     * the class of input that was received.  Implementations of this method
+     * must examine the bits in the source before processing the event.
+     * The following code example shows how this is done.
+     * </p>
+     * <code>
+     * public boolean onGenericMotionEvent(MotionEvent event) {
+     *     if ((event.getSource() &amp; InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+     *         float x = event.getX();
+     *         float y = event.getY();
+     *         // process the joystick motion
+     *         return true;
+     *     }
+     *     return super.onGenericMotionEvent(event);
+     * }
+     * </code>
+     *
+     * @param event The generic motion event being processed.
+     *
+     * @return Return true if you have consumed the event, false if you haven't.
+     * The default implementation always returns false.
+     */
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        return false;
+    }
+
     public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
         if (mDecor != null) {
             mWindowManager.updateViewLayout(mDecor, params);
@@ -704,6 +736,23 @@
         return onTrackballEvent(ev);
     }
 
+    /**
+     * Called to process generic motion events.  You can override this to
+     * intercept all generic motion events before they are dispatched to the
+     * window.  Be sure to call this implementation for generic motion events
+     * that should be handled normally.
+     *
+     * @param ev The generic motion event.
+     *
+     * @return boolean Return true if this event was consumed.
+     */
+    public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+        if (mWindow.superDispatchGenericMotionEvent(ev)) {
+            return true;
+        }
+        return onGenericMotionEvent(ev);
+    }
+
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         event.setClassName(getClass().getName());
         event.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 297d246..af410c4 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -24,13 +24,12 @@
 import android.database.CursorWrapper;
 import android.net.ConnectivityManager;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.Downloads;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
-import android.util.Log;
+import android.text.TextUtils;
 import android.util.Pair;
 
 import java.io.File;
@@ -53,7 +52,6 @@
  * download in a notification or from the downloads UI.
  */
 public class DownloadManager {
-    private static final String TAG = "DownloadManager";
 
     /**
      * An identifier for a particular download, unique across the system.  Clients use this ID to
@@ -387,6 +385,10 @@
             mUri = uri;
         }
 
+        Request(String uriString) {
+            mUri = Uri.parse(uriString);
+        }
+
         /**
          * Set the local destination for the downloaded file. Must be a file URI to a path on
          * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
@@ -1070,6 +1072,68 @@
             return null;
         }
     }
+
+    /**
+     * Adds a file to the downloads database system, so it could appear in Downloads App
+     * (and thus become eligible for management by the Downloads App).
+     * <p>
+     * It is helpful to make the file scannable by MediaScanner by setting the param
+     * isMediaScannerScannable to true. It makes the file visible in media managing
+     * applications such as Gallery App, which could be a useful purpose of using this API.
+     *
+     * @param title the title that would appear for this file in Downloads App.
+     * @param description the description that would appear for this file in Downloads App.
+     * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
+     * scanned by MediaScanner appear in the applications used to view media (for example,
+     * Gallery app).
+     * @param mimeType mimetype of the file.
+     * @param path absolute pathname to the file. The file should be world-readable, so that it can
+     * be managed by the Downloads App and any other app that is used to read it (for example,
+     * Gallery app to display the file, if the file contents represent a video/image).
+     * @param length length of the downloaded file
+     * @return  an ID for the download entry added to the downloads app, unique across the system
+     * This ID is used to make future calls related to this download.
+     */
+    public long completedDownload(String title, String description,
+            boolean isMediaScannerScannable, String mimeType, String path, long length) {
+        // make sure the input args are non-null/non-zero
+        validateArgumentIsNonEmpty("title", title);
+        validateArgumentIsNonEmpty("description", description);
+        validateArgumentIsNonEmpty("path", path);
+        validateArgumentIsNonEmpty("mimeType", mimeType);
+        if (length <= 0) {
+            throw new IllegalArgumentException(" invalid value for param: totalBytes");
+        }
+
+        // if there is already an entry with the given path name in downloads.db, return its id
+        Request request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD)
+                .setTitle(title)
+                .setDescription(description)
+                .setMimeType(mimeType);
+        ContentValues values = request.toContentValues(null);
+        values.put(Downloads.Impl.COLUMN_DESTINATION,
+                Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
+        values.put(Downloads.Impl._DATA, path);
+        values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS);
+        values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, length);
+        values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED,
+                (isMediaScannerScannable) ? Request.SCANNABLE_VALUE_YES :
+                        Request.SCANNABLE_VALUE_NO);
+        Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
+        if (downloadUri == null) {
+            return -1;
+        }
+        return Long.parseLong(downloadUri.getLastPathSegment());
+    }
+    private static final String NON_DOWNLOADMANAGER_DOWNLOAD =
+            "non-dwnldmngr-download-dont-retry2download";
+
+    private static void validateArgumentIsNonEmpty(String paramName, String val) {
+        if (TextUtils.isEmpty(val)) {
+            throw new IllegalArgumentException(paramName + " can't be null");
+        }
+    }
+
     /**
      * Get the DownloadProvider URI for the download with the given ID.
      */
@@ -1144,7 +1208,8 @@
         private String getLocalUri() {
             long destinationType = getLong(getColumnIndex(Downloads.Impl.COLUMN_DESTINATION));
             if (destinationType == Downloads.Impl.DESTINATION_FILE_URI ||
-                    destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
+                    destinationType == Downloads.Impl.DESTINATION_EXTERNAL ||
+                    destinationType == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
                 String localPath = getString(getColumnIndex(COLUMN_LOCAL_FILENAME));
                 if (localPath == null) {
                     return null;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d14cf4d..051ae9e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1663,6 +1663,16 @@
     public static final String SIP_SERVICE = "sip";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.hardware.UsbManager} for access to USB devices (as a USB host)
+     * and for controlling this device's behavior as a USB device.
+     *
+     * @see #getSystemService
+     * @see android.harware.UsbManager
+     */
+    public static final String USB_SERVICE = "usb";
+
+    /**
      * 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/SyncManager.java b/core/java/android/content/SyncManager.java
index 2fa2834..f45cf2a 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -1373,6 +1373,7 @@
         public void handleMessage(Message msg) {
             long earliestFuturePollTime = Long.MAX_VALUE;
             long nextPendingSyncTime = Long.MAX_VALUE;
+
             // Setting the value here instead of a method because we want the dumpsys logs
             // to have the most recent value used.
             try {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7676258..e8292cc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3085,11 +3085,7 @@
         if (!sCompatibilityModeEnabled) {
             ai.disableCompatibilityMode();
         }
-        if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
-            ai.enabled = true;
-        } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
-            ai.enabled = false;
-        }
+        ai.enabled = p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         return ai;
     }
 
diff --git a/core/java/android/hardware/IUsbManager.aidl b/core/java/android/hardware/IUsbManager.aidl
new file mode 100644
index 0000000..b50b6b9
--- /dev/null
+++ b/core/java/android/hardware/IUsbManager.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.hardware;
+
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+/** @hide */
+interface IUsbManager
+{
+    /* Returns a list of all currently attached USB devices */
+    void getDeviceList(out Bundle devices);
+    ParcelFileDescriptor openDevice(String deviceName);
+}
diff --git a/core/java/android/hardware/UsbConstants.java b/core/java/android/hardware/UsbConstants.java
new file mode 100644
index 0000000..29a335c
--- /dev/null
+++ b/core/java/android/hardware/UsbConstants.java
@@ -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.
+ */
+
+package android.hardware;
+
+/**
+ * Contains constants for the USB protocol.
+ * These constants correspond to definitions in linux/usb/ch9.h in the linux kernel.
+ */
+public final class UsbConstants {
+
+    public static final int USB_ENDPOINT_DIR_MASK = 0x80;
+    public static final int USB_DIR_OUT = 0;
+    public static final int USB_DIR_IN = 0x80;
+
+    public static final int USB_TYPE_MASK = (0x03 << 5);
+    public static final int USB_TYPE_STANDARD = (0x00 << 5);
+    public static final int USB_TYPE_CLASS = (0x01 << 5);
+    public static final int USB_TYPE_VENDOR = (0x02 << 5);
+    public static final int USB_TYPE_RESERVED = (0x03 << 5);
+
+    public static final int USB_ENDPOINT_NUMBER_MASK = 0x0f;
+
+    // flags for endpoint attributes
+    public static final int USB_ENDPOINT_XFERTYPE_MASK = 0x03;
+    public static final int USB_ENDPOINT_XFER_CONTROL = 0;
+    public static final int USB_ENDPOINT_XFER_ISOC = 1;
+    public static final int USB_ENDPOINT_XFER_BULK = 2;
+    public static final int USB_ENDPOINT_XFER_INT = 3;
+
+    public static final int USB_CLASS_PER_INTERFACE = 0;
+    public static final int USB_CLASS_AUDIO = 1;
+    public static final int USB_CLASS_COMM = 2;
+    public static final int USB_CLASS_HID = 3;
+    public static final int USB_CLASS_PHYSICA = 5;
+    public static final int USB_CLASS_STILL_IMAGE = 6;
+    public static final int USB_CLASS_PRINTER = 7;
+    public static final int USB_CLASS_MASS_STORAGE = 8;
+    public static final int USB_CLASS_HUB = 9;
+    public static final int USB_CLASS_CDC_DATA = 0x0a;
+    public static final int USB_CLASS_CSCID = 0x0b;
+    public static final int USB_CLASS_CONTENT_SEC = 0x0d;
+    public static final int USB_CLASS_VIDEO = 0x0e;
+    public static final int USB_CLASS_WIRELESS_CONTROLLER = 0xe0;
+    public static final int USB_CLASS_MISC = 0xef;
+    public static final int USB_CLASS_APP_SPEC = 0xfe;
+    public static final int USB_CLASS_VENDOR_SPEC = 0xff;
+    public static final int USB_SUBCLASS_VENDOR_SPEC = 0xff;
+
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/UsbDevice.aidl b/core/java/android/hardware/UsbDevice.aidl
new file mode 100644
index 0000000..6dfd43f
--- /dev/null
+++ b/core/java/android/hardware/UsbDevice.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 android.hardware;
+
+parcelable UsbDevice;
diff --git a/core/java/android/hardware/UsbDevice.java b/core/java/android/hardware/UsbDevice.java
new file mode 100644
index 0000000..e6b38be
--- /dev/null
+++ b/core/java/android/hardware/UsbDevice.java
@@ -0,0 +1,314 @@
+/*
+ * 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.hardware;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+
+
+/**
+ * A class representing a USB device.
+ */
+public final class UsbDevice implements Parcelable {
+
+    private static final String TAG = "UsbDevice";
+
+    private String mName;
+    private int mVendorId;
+    private int mProductId;
+    private int mClass;
+    private int mSubclass;
+    private int mProtocol;
+    private Parcelable[] mInterfaces;
+
+    // used by the JNI code
+    private int mNativeContext;
+
+    private UsbDevice() {
+    }
+
+
+    /**
+     * UsbDevice should only be instantiated by UsbService implementation
+     * @hide
+     */
+    public UsbDevice(String name, int vendorId, int productId,
+            int Class, int subClass, int protocol, Parcelable[] interfaces) {
+        mName = name;
+        mVendorId = vendorId;
+        mProductId = productId;
+        mClass = Class;
+        mSubclass = subClass;
+        mProtocol = protocol;
+        mInterfaces = interfaces;
+    }
+
+    /**
+     * Returns the name of the device.
+     * In the standard implementation, this is the path of the device file
+     * for the device in the usbfs file system.
+     *
+     * @return the device name
+     */
+    public String getDeviceName() {
+        return mName;
+    }
+
+    /**
+     * Returns a unique integer ID for the device.
+     * This is a convenience for clients that want to use an integer to represent
+     * the device, rather than the device name.
+     * IDs are not persistent across USB disconnects.
+     *
+     * @return the device ID
+     */
+    public int getDeviceId() {
+        return getDeviceId(mName);
+    }
+
+    /**
+     * Returns a vendor ID for the device.
+     *
+     * @return the device vendor ID
+     */
+    public int getVendorId() {
+        return mVendorId;
+    }
+
+    /**
+     * Returns a product ID for the device.
+     *
+     * @return the device product ID
+     */
+    public int getProductId() {
+        return mProductId;
+    }
+
+    /**
+     * Returns the devices's class field.
+     * Some useful constants for USB device classes can be found in
+     * {@link android.hardware.UsbConstants}
+     *
+     * @return the devices's class
+     */
+    public int getDeviceClass() {
+        return mClass;
+    }
+
+    /**
+     * Returns the device's subclass field.
+     *
+     * @return the device's subclass
+     */
+    public int getDeviceSubclass() {
+        return mSubclass;
+    }
+
+    /**
+     * Returns the device's subclass field.
+     *
+     * @return the device's protocol
+     */
+    public int getDeviceProtocol() {
+        return mProtocol;
+    }
+
+    /**
+     * Returns the number of {@link android.hardware.UsbInterface}s this device contains.
+     *
+     * @return the number of interfaces
+     */
+    public int getInterfaceCount() {
+        return mInterfaces.length;
+    }
+
+    /**
+     * Returns the {@link android.hardware.UsbInterface} at the given index.
+     *
+     * @return the interface
+     */
+    public UsbInterface getInterface(int index) {
+        return (UsbInterface)mInterfaces[index];
+    }
+
+    /* package */ boolean open(ParcelFileDescriptor pfd) {
+        return native_open(mName, pfd.getFileDescriptor());
+    }
+
+    /**
+     * Releases all system resources related to the device.
+     */
+    public void close() {
+        native_close();
+    }
+
+    /**
+     * Returns an integer file descriptor for the device, or
+     * -1 if the device is not opened.
+     * This is intended for passing to native code to access the device
+     */
+    public int getFileDescriptor() {
+        return native_get_fd();
+    }
+
+    /**
+     * Claims exclusive access to a {@link android.hardware.UsbInterface}.
+     * This must be done before sending or receiving data on any
+     * {@link android.hardware.UsbEndpoint}s belonging to the interface
+     * @param intf the interface to claim
+     * @param force true to disconnect kernel driver if necessary
+     * @return true if the interface was successfully claimed
+     */
+    public boolean claimInterface(UsbInterface intf, boolean force) {
+        return native_claim_interface(intf.getId(), force);
+    }
+
+    /**
+     * Releases exclusive access to a {@link android.hardware.UsbInterface}.
+     *
+     * @return true if the interface was successfully released
+     */
+    public boolean releaseInterface(UsbInterface intf) {
+        return native_release_interface(intf.getId());
+    }
+
+    /**
+     * Performs a control transaction on endpoint zero for this device.
+     *
+     * @param requestType request type for this transaction
+     * @param request request ID for this transaction
+     * @param value value field for this transaction
+     * @param index index field for this transaction
+     * @param buffer buffer for data portion of transaction,
+     * or null if no data needs to be sent or received
+     * @param length the length of the data to send or receive
+     * @return length of data transferred (or zero) for success,
+     * or negative value for failure
+     */
+    public int sendControlRequest(int requestType, int request, int value,
+            int index, byte[] buffer, int length) {
+        return native_control_request(requestType, request, value, index, buffer, length);
+    }
+
+    /**
+     * Waits for the result of a {@link android.hardware.UsbRequest#queue} operation
+     * Note that this may return requests queued on multiple {@link android.hardware.UsbEndpoint}s.
+     * When multiple endpoints are in use, {@link android.hardware.UsbRequest#getEndpoint} and
+     * {@link android.hardware.UsbRequest#getClientData} can be useful in determining how to process
+     * the result of this function.
+     *
+     * @return a completed USB request, or null if an error occurred
+     */
+    public UsbRequest requestWait() {
+        UsbRequest request = native_request_wait();
+        if (request != null) {
+            request.dequeue();
+        }
+        return request;
+    }
+
+    /**
+     * Returns the serial number for the device.
+     * This will return null if the device has not been opened.
+     *
+     * @return the device serial number
+     */
+    public String getSerial() {
+        return native_get_serial();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof UsbDevice) {
+            return ((UsbDevice)o).mName.equals(mName);
+        } else if (o instanceof String) {
+            return ((String)o).equals(mName);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId +
+                ",mProductId=" + mProductId + ",mClass=" + mClass +
+                ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
+                ",mInterfaces=" + mInterfaces + "]";
+    }
+
+    public static final Parcelable.Creator<UsbDevice> CREATOR =
+        new Parcelable.Creator<UsbDevice>() {
+        public UsbDevice createFromParcel(Parcel in) {
+            String name = in.readString();
+            int vendorId = in.readInt();
+            int productId = in.readInt();
+            int clasz = in.readInt();
+            int subClass = in.readInt();
+            int protocol = in.readInt();
+            Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader());
+            UsbDevice result = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol, interfaces);
+            for (int i = 0; i < interfaces.length; i++) {
+                ((UsbInterface)interfaces[i]).setDevice(result);
+            }
+            return result;
+        }
+
+        public UsbDevice[] newArray(int size) {
+            return new UsbDevice[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mName);
+        parcel.writeInt(mVendorId);
+        parcel.writeInt(mProductId);
+        parcel.writeInt(mClass);
+        parcel.writeInt(mSubclass);
+        parcel.writeInt(mProtocol);
+        parcel.writeParcelableArray(mInterfaces, 0);
+   }
+
+    public static int getDeviceId(String name) {
+        return native_get_device_id(name);
+    }
+
+    public static String getDeviceName(int id) {
+        return native_get_device_name(id);
+    }
+
+    private native boolean native_open(String deviceName, FileDescriptor pfd);
+    private native void native_close();
+    private native int native_get_fd();
+    private native boolean native_claim_interface(int interfaceID, boolean force);
+    private native boolean native_release_interface(int interfaceID);
+    private native int native_control_request(int requestType, int request, int value,
+            int index, byte[] buffer, int length);
+    private native UsbRequest native_request_wait();
+    private native String native_get_serial();
+
+    private static native int native_get_device_id(String name);
+    private static native String native_get_device_name(int id);
+}
diff --git a/core/java/android/hardware/UsbEndpoint.aidl b/core/java/android/hardware/UsbEndpoint.aidl
new file mode 100644
index 0000000..51fc67b
--- /dev/null
+++ b/core/java/android/hardware/UsbEndpoint.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 android.hardware;
+
+parcelable UsbEndpoint;
diff --git a/core/java/android/hardware/UsbEndpoint.java b/core/java/android/hardware/UsbEndpoint.java
new file mode 100644
index 0000000..8d4099d
--- /dev/null
+++ b/core/java/android/hardware/UsbEndpoint.java
@@ -0,0 +1,176 @@
+/*
+ * 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.hardware;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing an endpoint on a {@link android.hardware.UsbInterface}.
+ */
+public final class UsbEndpoint implements Parcelable {
+
+    private int mAddress;
+    private int mAttributes;
+    private int mMaxPacketSize;
+    private int mInterval;
+    private UsbInterface mInterface;
+
+    private UsbEndpoint() {
+    }
+
+    /**
+     * UsbEndpoint should only be instantiated by UsbService implementation
+     * @hide
+     */
+    public UsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
+        mAddress = address;
+        mAttributes = attributes;
+        mMaxPacketSize = maxPacketSize;
+        mInterval = interval;
+    }
+
+    /**
+     * Returns the endpoint's address field.
+     *
+     * @return the endpoint's address
+     */
+    public int getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * Extracts the endpoint's endpoint number from its address
+     *
+     * @return the endpoint's endpoint number
+     */
+    public int getEndpointNumber() {
+        return mAddress & UsbConstants.USB_ENDPOINT_NUMBER_MASK;
+    }
+
+    /**
+     * Returns the endpoint's direction.
+     * Returns {@link android.hardware.UsbConstants#USB_DIR_OUT}
+     * if the direction is host to device, and
+     * {@link android.hardware.UsbConstants#USB_DIR_IN} if the
+     * direction is device to host.
+     *
+     * @return the endpoint's direction
+     */
+    public int getDirection() {
+        return mAddress & UsbConstants.USB_ENDPOINT_DIR_MASK;
+    }
+
+    /**
+     * Returns the endpoint's attributes field.
+     *
+     * @return the endpoint's attributes
+     */
+    public int getAttributes() {
+        return mAttributes;
+    }
+
+    /**
+     * Returns the endpoint's type.
+     * Possible results are:
+     * <ul>
+     * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero)
+     * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint)
+     * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint)
+     * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint)
+     * </ul>
+     *
+     * @return the endpoint's type
+     */
+    public int getType() {
+        return mAttributes & UsbConstants.USB_ENDPOINT_XFERTYPE_MASK;
+    }
+
+    /**
+     * Returns the endpoint's maximum packet size.
+     *
+     * @return the endpoint's maximum packet size
+     */
+    public int getMaxPacketSize() {
+        return mMaxPacketSize;
+    }
+
+    /**
+     * Returns the endpoint's interval field.
+     *
+     * @return the endpoint's interval
+     */
+    public int getInterval() {
+        return mInterval;
+    }
+
+    /**
+     * Returns the {@link android.hardware.UsbInterface} this endpoint belongs to.
+     *
+     * @return the endpoint's interface
+     */
+    public UsbInterface getInterface() {
+        return mInterface;
+    }
+
+    /**
+     * Returns the {@link android.hardware.UsbDevice} this endpoint belongs to.
+     *
+     * @return the endpoint's device
+     */
+    public UsbDevice getDevice() {
+        return mInterface.getDevice();
+    }
+
+    // only used for parcelling
+    /* package */ void setInterface(UsbInterface intf) {
+        mInterface = intf;
+    }
+
+    @Override
+    public String toString() {
+        return "UsbEndpoint[mAddress=" + mAddress + ",mAttributes=" + mAttributes +
+                ",mMaxPacketSize=" + mMaxPacketSize + ",mInterval=" + mInterval +"]";
+    }
+
+    public static final Parcelable.Creator<UsbEndpoint> CREATOR =
+        new Parcelable.Creator<UsbEndpoint>() {
+        public UsbEndpoint createFromParcel(Parcel in) {
+            int address = in.readInt();
+            int attributes = in.readInt();
+            int maxPacketSize = in.readInt();
+            int interval = in.readInt();
+            return new UsbEndpoint(address, attributes, maxPacketSize, interval);
+        }
+
+        public UsbEndpoint[] newArray(int size) {
+            return new UsbEndpoint[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mAddress);
+        parcel.writeInt(mAttributes);
+        parcel.writeInt(mMaxPacketSize);
+        parcel.writeInt(mInterval);
+   }
+}
diff --git a/core/java/android/hardware/UsbInterface.aidl b/core/java/android/hardware/UsbInterface.aidl
new file mode 100644
index 0000000..a715ccd
--- /dev/null
+++ b/core/java/android/hardware/UsbInterface.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 android.hardware;
+
+parcelable UsbInterface;
diff --git a/core/java/android/hardware/UsbInterface.java b/core/java/android/hardware/UsbInterface.java
new file mode 100644
index 0000000..deef81f
--- /dev/null
+++ b/core/java/android/hardware/UsbInterface.java
@@ -0,0 +1,159 @@
+/*
+ * 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.hardware;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing an interface on a {@link android.hardware.UsbDevice}.
+ */
+public final class UsbInterface implements Parcelable {
+
+    private int mId;
+    private int mClass;
+    private int mSubclass;
+    private int mProtocol;
+    private UsbDevice mDevice;
+    private Parcelable[] mEndpoints;
+
+    private UsbInterface() {
+    }
+
+    /**
+     * UsbInterface should only be instantiated by UsbService implementation
+     * @hide
+     */
+    public UsbInterface(int id, int Class, int subClass, int protocol,
+            Parcelable[] endpoints) {
+        mId = id;
+        mClass = Class;
+        mSubclass = subClass;
+        mProtocol = protocol;
+        mEndpoints = endpoints;
+    }
+
+    /**
+     * Returns the interface's ID field.
+     *
+     * @return the interface's ID
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the interface's class field.
+     * Some useful constants for USB classes can be found in
+     * {@link android.hardware.UsbConstants}
+     *
+     * @return the interface's class
+     */
+    public int getInterfaceClass() {
+        return mClass;
+    }
+
+    /**
+     * Returns the interface's subclass field.
+     *
+     * @return the interface's subclass
+     */
+    public int getInterfaceSubclass() {
+        return mSubclass;
+    }
+
+    /**
+     * Returns the interface's protocol field.
+     *
+     * @return the interface's protocol
+     */
+    public int getInterfaceProtocol() {
+        return mProtocol;
+    }
+
+    /**
+     * Returns the number of {@link android.hardware.UsbEndpoint}s this interface contains.
+     *
+     * @return the number of endpoints
+     */
+    public int getEndpointCount() {
+        return mEndpoints.length;
+    }
+
+    /**
+     * Returns the {@link android.hardware.UsbEndpoint} at the given index.
+     *
+     * @return the endpoint
+     */
+    public UsbEndpoint getEndpoint(int index) {
+        return (UsbEndpoint)mEndpoints[index];
+    }
+
+    /**
+     * Returns the {@link android.hardware.UsbDevice} this interface belongs to.
+     *
+     * @return the interface's device
+     */
+    public UsbDevice getDevice() {
+        return mDevice;
+    }
+
+    // only used for parcelling
+    /* package */ void setDevice(UsbDevice device) {
+        mDevice = device;
+    }
+
+    @Override
+    public String toString() {
+        return "UsbInterface[mId=" + mId + ",mClass=" + mClass +
+                ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
+                ",mEndpoints=" + mEndpoints + "]";
+    }
+
+    public static final Parcelable.Creator<UsbInterface> CREATOR =
+        new Parcelable.Creator<UsbInterface>() {
+        public UsbInterface createFromParcel(Parcel in) {
+            int id = in.readInt();
+            int Class = in.readInt();
+            int subClass = in.readInt();
+            int protocol = in.readInt();
+            Parcelable[] endpoints = in.readParcelableArray(UsbEndpoint.class.getClassLoader());
+            UsbInterface result = new UsbInterface(id, Class, subClass, protocol, endpoints);
+            for (int i = 0; i < endpoints.length; i++) {
+                ((UsbEndpoint)endpoints[i]).setInterface(result);
+            }
+            return result;
+        }
+
+        public UsbInterface[] newArray(int size) {
+            return new UsbInterface[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mId);
+        parcel.writeInt(mClass);
+        parcel.writeInt(mSubclass);
+        parcel.writeInt(mProtocol);
+        parcel.writeParcelableArray(mEndpoints, 0);
+   }
+}
diff --git a/core/java/android/hardware/UsbManager.java b/core/java/android/hardware/UsbManager.java
index 18790d2..8fad210 100644
--- a/core/java/android/hardware/UsbManager.java
+++ b/core/java/android/hardware/UsbManager.java
@@ -17,17 +17,31 @@
 
 package android.hardware;
 
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.util.HashMap;
 
 /**
- * Class for accessing USB state information.
- * @hide
+ * This class allows you to access the state of USB, both in host and device mode.
+ *
+ * <p>You can obtain an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+ *
+ * {@samplecode
+ * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ * }
  */
 public class UsbManager {
+    private static final String TAG = "UsbManager";
+
    /**
-     * Broadcast Action:  A sticky broadcast for USB state change events.
+     * Broadcast Action:  A sticky broadcast for USB state change events when in device mode.
      *
      * This is a sticky broadcast for clients that includes USB connected/disconnected state,
      * the USB configuration that is currently set and a bundle containing name/value pairs
@@ -39,6 +53,22 @@
     public static final String ACTION_USB_STATE =
             "android.hardware.action.USB_STATE";
 
+   /**
+     * Broadcast Action:  A broadcast for USB device attached event.
+     *
+     * This intent is sent when a USB device is attached to the USB bus when in host mode.
+     */
+    public static final String ACTION_USB_DEVICE_ATTACHED =
+            "android.hardware.action.USB_DEVICE_ATTACHED";
+
+   /**
+     * Broadcast Action:  A broadcast for USB device detached event.
+     *
+     * This intent is sent when a USB device is detached from the USB bus when in host mode.
+     */
+    public static final String ACTION_USB_DEVICE_DETACHED =
+            "android.hardware.action.USB_DEVICE_DETACHED";
+
     /**
      * Boolean extra indicating whether USB is connected or disconnected.
      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
@@ -87,6 +117,103 @@
      */
     public static final String USB_FUNCTION_DISABLED = "disabled";
 
+    /**
+     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
+     * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
+     * containing the device's ID (String).
+     */
+    public static final String EXTRA_DEVICE_NAME = "device_name";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+     * containing the device's vendor ID (int).
+     */
+    public static final String EXTRA_VENDOR_ID = "vendor_id";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+     * containing the device's product ID (int).
+     */
+    public static final String EXTRA_PRODUCT_ID = "product_id";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+     * containing the device's class (int).
+     */
+    public static final String EXTRA_DEVICE_CLASS = "device_class";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+     * containing the device's class (int).
+     */
+    public static final String EXTRA_DEVICE_SUBCLASS = "device_subclass";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+     * containing the device's class (int).
+     */
+    public static final String EXTRA_DEVICE_PROTOCOL = "device_protocol";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+     * containing the UsbDevice object for the device.
+     */
+    public static final String EXTRA_DEVICE = "device";
+
+    private IUsbManager mService;
+
+    /**
+     * {@hide}
+     */
+    public UsbManager(IUsbManager service) {
+        mService = service;
+    }
+
+    /**
+     * Returns a HashMap containing all USB devices currently attached.
+     * USB device name is the key for the returned HashMap.
+     * The result will be empty if no devices are attached, or if
+     * USB host mode is inactive or unsupported.
+     *
+     * @return HashMap containing all connected USB devices.
+     */
+    public HashMap<String,UsbDevice> getDeviceList() {
+        Bundle bundle = new Bundle();
+        try {
+            mService.getDeviceList(bundle);
+            HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
+            for (String name : bundle.keySet()) {
+                result.put(name, (UsbDevice)bundle.get(name));
+            }
+            return result;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getDeviceList", e);
+            return null;
+        }
+    }
+
+    /**
+     * Opens the device so it can be used to send and receive
+     * data using {@link android.hardware.UsbRequest}.
+     *
+     * @param device the device to open
+     * @return true if we successfully opened the device
+     */
+    public boolean openDevice(UsbDevice device) {
+        try {
+            ParcelFileDescriptor pfd = mService.openDevice(device.getDeviceName());
+            if (pfd == null) {
+                return false;
+            }
+            boolean result = device.open(pfd);
+            pfd.close();
+            return result;
+        } catch (Exception e) {
+            Log.e(TAG, "exception in UsbManager.openDevice", e);
+            return false;
+        }
+    }
+
     private static File getFunctionEnableFile(String function) {
         return new File("/sys/class/usb_composite/" + function + "/enable");
     }
@@ -94,6 +221,9 @@
     /**
      * Returns true if the specified USB function is supported by the kernel.
      * Note that a USB function maybe supported but disabled.
+     *
+     * @param function name of the USB function
+     * @return true if the USB function is supported.
      */
     public static boolean isFunctionSupported(String function) {
         return getFunctionEnableFile(function).exists();
@@ -101,6 +231,9 @@
 
     /**
      * Returns true if the specified USB function is currently enabled.
+     *
+     * @param function name of the USB function
+     * @return true if the USB function is enabled.
      */
     public static boolean isFunctionEnabled(String function) {
         try {
diff --git a/core/java/android/hardware/UsbRequest.java b/core/java/android/hardware/UsbRequest.java
new file mode 100644
index 0000000..ae3a289
--- /dev/null
+++ b/core/java/android/hardware/UsbRequest.java
@@ -0,0 +1,177 @@
+/*
+ * 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.hardware;
+
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A class representing USB request packet.
+ * This can be used for both reading and writing data to or from a
+ * {@link android.hardware.UsbDevice}.
+ * UsbRequests are sent asynchronously via {@link #queue} and the results
+ * are read by {@link android.hardware.UsbDevice#requestWait}.
+ */
+public class UsbRequest {
+
+    private static final String TAG = "UsbRequest";
+
+    // used by the JNI code
+    private int mNativeContext;
+
+    private UsbEndpoint mEndpoint;
+
+    // for temporarily saving current buffer across queue and dequeue
+    private ByteBuffer mBuffer;
+    private int mLength;
+
+    // for client use
+    private Object mClientData;
+
+    public UsbRequest() {
+    }
+
+    /**
+     * Initializes the request so it can read or write data on the given endpoint.
+     * Whether the request allows reading or writing depends on the direction of the endpoint.
+     *
+     * @param endpoint the endpoint to be used for this request.
+     * @return true if the request was successfully opened.
+     */
+    public boolean initialize(UsbEndpoint endpoint) {
+        mEndpoint = endpoint;
+        return native_init(endpoint.getDevice(),
+                endpoint.getAddress(), endpoint.getAttributes(),
+                endpoint.getMaxPacketSize(), endpoint.getInterval());
+    }
+
+    /**
+     * Releases all resources related to this request.
+     */
+    public void close() {
+        mEndpoint = null;
+        native_close();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mEndpoint != null) {
+                Log.v(TAG, "endpoint still open in finalize(): " + this);
+                close();
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Returns the endpoint for the request, or null if the request is not opened.
+     *
+     * @return the request's endpoint
+     */
+    public UsbEndpoint getEndpoint() {
+        return mEndpoint;
+    }
+
+    /**
+     * Returns the client data for the request.
+     * This can be used in conjunction with {@link #setClientData}
+     * to associate another object with this request, which can be useful for
+     * maintaining state between calls to {@link #queue} and
+     * {@link android.hardware.UsbDevice#requestWait}
+     *
+     * @return the client data for the request
+     */
+    public Object getClientData() {
+        return mClientData;
+    }
+
+    /**
+     * Sets the client data for the request.
+     * This can be used in conjunction with {@link #getClientData}
+     * to associate another object with this request, which can be useful for
+     * maintaining state between calls to {@link #queue} and
+     * {@link android.hardware.UsbDevice#requestWait}
+     *
+     * @param data the client data for the request
+     */
+    public void setClientData(Object data) {
+        mClientData = data;
+    }
+
+    /**
+     * Queues the request to send or receive data on its endpoint.
+     * For OUT endpoints, the given buffer data will be sent on the endpoint.
+     * For IN endpoints, the endpoint will attempt to read the given number of bytes
+     * into the specified buffer.
+     * If the queueing operation is successful, we return true and the result will be
+     * returned via {@link android.hardware.UsbDevice#requestWait}
+     *
+     * @param buffer the buffer containing the bytes to write, or location to store
+     * the results of a read
+     * @param length number of bytes to read or write
+     * @return true if the queueing operation succeeded
+     */
+    public boolean queue(ByteBuffer buffer, int length) {
+        boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
+        boolean result;
+        if (buffer.isDirect()) {
+            result = native_queue_direct(buffer, length, out);
+        } else if (buffer.hasArray()) {
+            result = native_queue_array(buffer.array(), length, out);
+        } else {
+            throw new IllegalArgumentException("buffer is not direct and has no array");
+        }
+        if (result) {
+            // save our buffer for when the request has completed
+            mBuffer = buffer;
+            mLength = length;
+        }
+        return result;
+    }
+
+    /* package */ void dequeue() {
+        boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
+        if (mBuffer.isDirect()) {
+            native_dequeue_direct();
+        } else {
+            native_dequeue_array(mBuffer.array(), mLength, out);
+        }
+        mBuffer = null;
+        mLength = 0;
+    }
+
+    /**
+     * Cancels a pending queue operation.
+     *
+     * @return true if cancelling succeeded
+     */
+    public boolean cancel() {
+        return native_cancel();
+    }
+
+    private native boolean native_init(UsbDevice device, int ep_address, int ep_attributes,
+            int ep_max_packet_size, int ep_interval);
+    private native void native_close();
+    private native boolean native_queue_array(byte[] buffer, int length, boolean out);
+    private native void native_dequeue_array(byte[] buffer, int length, boolean out);
+    private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out);
+    private native void native_dequeue_direct();
+    private native boolean native_cancel();
+}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 8a653dd..b0c974c 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -104,45 +104,20 @@
     public native static String getDhcpError();
 
     /**
-     * When static IP configuration has been specified, configure the network
-     * interface according to the values supplied.
-     * @param interfaceName the name of the interface to configure
-     * @param ipInfo the IP address, default gateway, and DNS server addresses
-     * with which to configure the interface.
-     * @return {@code true} for success, {@code false} for failure
-     */
-    public static boolean configureInterface(String interfaceName, DhcpInfo ipInfo) {
-        return configureNative(interfaceName,
-            ipInfo.ipAddress,
-            ipInfo.netmask,
-            ipInfo.gateway,
-            ipInfo.dns1,
-            ipInfo.dns2);
-    }
-
-    private native static boolean configureNative(
-        String interfaceName, int ipAddress, int netmask, int gateway, int dns1, int dns2);
-
-    /**
      * Convert a IPv4 address from an integer to an InetAddress.
-     * @param hostAddr is an Int corresponding to the IPv4 address in network byte order
-     * @return the IP address as an {@code InetAddress}, returns null if
-     * unable to convert or if the int is an invalid address.
+     * @param hostAddress an int corresponding to the IPv4 address in network byte order
      */
     public static InetAddress intToInetAddress(int hostAddress) {
-        InetAddress inetAddress;
         byte[] addressBytes = { (byte)(0xff & hostAddress),
                                 (byte)(0xff & (hostAddress >> 8)),
                                 (byte)(0xff & (hostAddress >> 16)),
                                 (byte)(0xff & (hostAddress >> 24)) };
 
         try {
-           inetAddress = InetAddress.getByAddress(addressBytes);
-        } catch(UnknownHostException e) {
-           return null;
+           return InetAddress.getByAddress(addressBytes);
+        } catch (UnknownHostException e) {
+           throw new AssertionError();
         }
-
-        return inetAddress;
     }
 
     /**
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 013edd3..5557a0d 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -466,6 +466,12 @@
         public static final int DESTINATION_SYSTEMCACHE_PARTITION = 5;
 
         /**
+         * This download was completed by the caller (i.e., NOT downloadmanager)
+         * and caller wants to have this download displayed in Downloads App.
+         */
+        public static final int DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD = 6;
+
+        /**
          * This download is allowed to run.
          */
         public static final int CONTROL_RUN = 0;
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index baaa3ce..5ae65df 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -17,32 +17,33 @@
 package android.text.format;
 
 import android.content.Context;
+import android.net.NetworkUtils;
 
 /**
  * Utility class to aid in formatting common values that are not covered
- * by the standard java.util.Formatter.
+ * by {@link java.util.Formatter}
  */
 public final class Formatter {
 
     /**
      * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc
-     * 
+     *
      * @param context Context to use to load the localized units
-     * @param number size value to be formated
-     * @return formated string with the number
+     * @param number size value to be formatted
+     * @return formatted string with the number
      */
     public static String formatFileSize(Context context, long number) {
         return formatFileSize(context, number, false);
     }
-    
+
     /**
      * Like {@link #formatFileSize}, but trying to generate shorter numbers
-     * (showing fewer digits of precisin).
+     * (showing fewer digits of precision).
      */
     public static String formatShortFileSize(Context context, long number) {
         return formatFileSize(context, number, true);
     }
-    
+
     private static String formatFileSize(Context context, long number, boolean shorter) {
         if (context == null) {
             return "";
@@ -92,21 +93,21 @@
             getString(com.android.internal.R.string.fileSizeSuffix,
                       value, context.getString(suffix));
     }
-    
+
     /**
      * Returns a string in the canonical IP format ###.###.###.### from a packed integer containing
      * the IP address.  The IP address is expected to be in little-endian format (LSB first). That
      * is, 0x01020304 will return "4.3.2.1".
-     * 
-     * @param addr the IP address as a packed integer with LSB first.
+     *
+     * @param ipv4Address the IP address as a packed integer with LSB first.
      * @return string with canonical IP address format.
+     *
+     * @deprecated this method doesn't support IPv6 addresses. Prefer {@link
+     *     java.net.InetAddress#getHostAddress()}, which supports both IPv4 and
+     *     IPv6 addresses.
      */
-    public static String formatIpAddress(int addr) {
-        StringBuffer buf = new StringBuffer();
-        buf.append(addr  & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff);
-        return buf.toString();
+    @Deprecated
+    public static String formatIpAddress(int ipv4Address) {
+        return NetworkUtils.intToInetAddress(ipv4Address).getHostAddress();
     }
 }
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index dd04975..e799f76 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -98,7 +98,16 @@
      * Use {@link #getMotionRange} to query the range of positions.
      */
     public static final int SOURCE_CLASS_POSITION = 0x00000008;
-    
+
+    /**
+     * The input source is a joystick.
+     *
+     * A {@link MotionEvent} should be interpreted as absolute joystick movements.
+     *
+     * Use {@link #getMotionRange} to query the range of positions.
+     */
+    public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
+
     /**
      * The input source is unknown.
      */
@@ -117,7 +126,15 @@
      * @see #SOURCE_CLASS_BUTTON
      */
     public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON;
-    
+
+    /**
+     * The input source is a game pad.
+     * (It may also be a {@link #SOURCE_JOYSTICK}).
+     *
+     * @see #SOURCE_CLASS_BUTTON
+     */
+    public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON;
+
     /**
      * The input source is a touch screen pointing device.
      * 
@@ -148,7 +165,15 @@
      * @see #SOURCE_CLASS_POSITION
      */
     public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
-    
+
+    /**
+     * The input source is a joystick.
+     * (It may also be a {@link #SOURCE_GAMEPAD}).
+     *
+     * @see #SOURCE_CLASS_JOYSTICK
+     */
+    public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK;
+
     /**
      * A special input source constant that is used when filtering input devices
      * to match devices that provide any type of input source.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 941331a..766969a 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -533,8 +533,40 @@
     /** Key code constant: App switch key.
      * Should bring up the application switcher dialog. */
     public static final int KEYCODE_APP_SWITCH      = 187;
+    /** Key code constant: Generic Game Pad Button #1.*/
+    public static final int KEYCODE_BUTTON_1        = 188;
+    /** Key code constant: Generic Game Pad Button #2.*/
+    public static final int KEYCODE_BUTTON_2        = 189;
+    /** Key code constant: Generic Game Pad Button #3.*/
+    public static final int KEYCODE_BUTTON_3        = 190;
+    /** Key code constant: Generic Game Pad Button #4.*/
+    public static final int KEYCODE_BUTTON_4        = 191;
+    /** Key code constant: Generic Game Pad Button #5.*/
+    public static final int KEYCODE_BUTTON_5        = 192;
+    /** Key code constant: Generic Game Pad Button #6.*/
+    public static final int KEYCODE_BUTTON_6        = 193;
+    /** Key code constant: Generic Game Pad Button #7.*/
+    public static final int KEYCODE_BUTTON_7        = 194;
+    /** Key code constant: Generic Game Pad Button #8.*/
+    public static final int KEYCODE_BUTTON_8        = 195;
+    /** Key code constant: Generic Game Pad Button #9.*/
+    public static final int KEYCODE_BUTTON_9        = 196;
+    /** Key code constant: Generic Game Pad Button #10.*/
+    public static final int KEYCODE_BUTTON_10       = 197;
+    /** Key code constant: Generic Game Pad Button #11.*/
+    public static final int KEYCODE_BUTTON_11       = 198;
+    /** Key code constant: Generic Game Pad Button #12.*/
+    public static final int KEYCODE_BUTTON_12       = 199;
+    /** Key code constant: Generic Game Pad Button #13.*/
+    public static final int KEYCODE_BUTTON_13       = 200;
+    /** Key code constant: Generic Game Pad Button #14.*/
+    public static final int KEYCODE_BUTTON_14       = 201;
+    /** Key code constant: Generic Game Pad Button #15.*/
+    public static final int KEYCODE_BUTTON_15       = 202;
+    /** Key code constant: Generic Game Pad Button #16.*/
+    public static final int KEYCODE_BUTTON_16       = 203;
 
-    private static final int LAST_KEYCODE           = KEYCODE_APP_SWITCH;
+    private static final int LAST_KEYCODE           = KEYCODE_BUTTON_16;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
@@ -741,6 +773,22 @@
         "KEYCODE_PROG_YELLOW",
         "KEYCODE_PROG_BLUE",
         "KEYCODE_APP_SWITCH",
+        "KEYCODE_BUTTON_1",
+        "KEYCODE_BUTTON_2",
+        "KEYCODE_BUTTON_3",
+        "KEYCODE_BUTTON_4",
+        "KEYCODE_BUTTON_5",
+        "KEYCODE_BUTTON_6",
+        "KEYCODE_BUTTON_7",
+        "KEYCODE_BUTTON_8",
+        "KEYCODE_BUTTON_9",
+        "KEYCODE_BUTTON_10",
+        "KEYCODE_BUTTON_11",
+        "KEYCODE_BUTTON_12",
+        "KEYCODE_BUTTON_13",
+        "KEYCODE_BUTTON_14",
+        "KEYCODE_BUTTON_15",
+        "KEYCODE_BUTTON_16",
     };
 
     // Symbolic names of all metakeys in bit order from least significant to most significant.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index db2cd50..0a5c5c6 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -26,7 +26,8 @@
  * class may hold either absolute or relative movements, depending on what
  * it is being used for.
  * <p>
- * On pointing devices such as touch screens, pointer coordinates specify absolute
+ * On pointing devices with source class {@link InputDevice#SOURCE_CLASS_POINTER}
+ * such as touch screens, the pointer coordinates specify absolute
  * positions such as view X/Y coordinates.  Each complete gesture is represented
  * by a sequence of motion events with actions that describe pointer state transitions
  * and movements.  A gesture starts with a motion event with {@link #ACTION_DOWN}
@@ -38,11 +39,18 @@
  * by a motion event with {@link #ACTION_UP} or when gesture is canceled
  * with {@link #ACTION_CANCEL}.
  * </p><p>
- * On trackballs, the pointer coordinates specify relative movements as X/Y deltas.
+ * On trackball devices with source class {@link InputDevice#SOURCE_CLASS_TRACKBALL},
+ * the pointer coordinates specify relative movements as X/Y deltas.
  * A trackball gesture consists of a sequence of movements described by motion
  * events with {@link #ACTION_MOVE} interspersed with occasional {@link #ACTION_DOWN}
  * or {@link #ACTION_UP} motion events when the trackball button is pressed or released.
  * </p><p>
+ * On joystick devices with source class {@link InputDevice#SOURCE_CLASS_JOYSTICK},
+ * the pointer coordinates specify the absolute position of the joystick axes.
+ * The joystick axis values are normalized to a range of -1.0 to 1.0 where 0.0 corresponds
+ * to the center position.  More information about the set of available axes and the
+ * range of motion can be obtained using {@link InputDevice#getMotionRange}.
+ * </p><p>
  * Motion events always report movements for all pointers at once.  The number
  * of pointers only ever changes by one as individual pointers go up and down,
  * except when the gesture is canceled.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6d5fd2c..27a71c5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4517,6 +4517,16 @@
     }
 
     /**
+     * Pass a generic motion event down to the focused view.
+     *
+     * @param event The motion event to be dispatched.
+     * @return True if the event was handled by the view, false otherwise.
+     */
+    public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        return onGenericMotionEvent(event);
+    }
+
+    /**
      * Called when the window containing this view gains or loses window focus.
      * ViewGroups should override to route to their children.
      *
@@ -5023,6 +5033,37 @@
     }
 
     /**
+     * Implement this method to handle generic motion events.
+     * <p>
+     * Generic motion events are dispatched to the focused view to describe
+     * the motions of input devices such as joysticks.  The
+     * {@link MotionEvent#getSource() source} of the motion event specifies
+     * the class of input that was received.  Implementations of this method
+     * must examine the bits in the source before processing the event.
+     * The following code example shows how this is done.
+     * </p>
+     * <code>
+     * public boolean onGenericMotionEvent(MotionEvent event) {
+     *     if ((event.getSource() &amp; InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+     *         float x = event.getX();
+     *         float y = event.getY();
+     *         // process the joystick motion
+     *         return true;
+     *     }
+     *     return super.onGenericMotionEvent(event);
+     * }
+     * </code>
+     *
+     * @param event The generic motion event being processed.
+     *
+     * @return Return true if you have consumed the event, false if you haven't.
+     * The default implementation always returns false.
+     */
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        return false;
+    }
+
+    /**
      * Implement this method to handle touch screen motion events.
      *
      * @param event The motion event.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d6c8ad6..19f1f7a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1140,6 +1140,19 @@
      * {@inheritDoc}
      */
     @Override
+    public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
+            return super.dispatchGenericMotionEvent(event);
+        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
+            return mFocused.dispatchGenericMotionEvent(event);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if (!onFilterTouchEventForSecurity(ev)) {
             return false;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index d932141..6bc5e8a 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -128,6 +128,11 @@
     final TrackballAxis mTrackballAxisX = new TrackballAxis();
     final TrackballAxis mTrackballAxisY = new TrackballAxis();
 
+    int mLastJoystickXDirection;
+    int mLastJoystickYDirection;
+    int mLastJoystickXKeyCode;
+    int mLastJoystickYKeyCode;
+
     final int[] mTmpLocation = new int[2];
 
     final TypedValue mTmpValue = new TypedValue();
@@ -1902,6 +1907,7 @@
     public final static int DISPATCH_DRAG_EVENT = 1015;
     public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
     public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017;
+    public final static int DISPATCH_GENERIC_MOTION = 1018;
 
     @Override
     public void handleMessage(Message msg) {
@@ -1938,6 +1944,9 @@
         case DISPATCH_TRACKBALL:
             deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0);
             break;
+        case DISPATCH_GENERIC_MOTION:
+            deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0);
+            break;
         case DISPATCH_APP_VISIBILITY:
             handleAppVisibility(msg.arg1 != 0);
             break;
@@ -2449,6 +2458,102 @@
         }
     }
 
+    private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
+        final int source = event.getSource();
+        final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
+
+        // If there is no view, then the event will not be handled.
+        if (mView == null || !mAdded) {
+            if (isJoystick) {
+                updateJoystickDirection(event, false);
+            }
+            finishGenericMotionEvent(event, sendDone, false);
+            return;
+        }
+
+        // Deliver the event to the view.
+        if (mView.dispatchGenericMotionEvent(event)) {
+            ensureTouchMode(false);
+            if (isJoystick) {
+                updateJoystickDirection(event, false);
+            }
+            finishGenericMotionEvent(event, sendDone, true);
+            return;
+        }
+
+        if (isJoystick) {
+            // Translate the joystick event into DPAD keys and try to deliver those.
+            updateJoystickDirection(event, true);
+            finishGenericMotionEvent(event, sendDone, true);
+        } else {
+            finishGenericMotionEvent(event, sendDone, false);
+        }
+    }
+
+    private void finishGenericMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
+        event.recycle();
+        if (sendDone) {
+            finishInputEvent(handled);
+        }
+    }
+
+    private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
+        final long time = event.getEventTime();
+        final int metaState = event.getMetaState();
+        final int deviceId = event.getDeviceId();
+        final int source = event.getSource();
+        final int xDirection = joystickAxisValueToDirection(event.getX());
+        final int yDirection = joystickAxisValueToDirection(event.getY());
+
+        if (xDirection != mLastJoystickXDirection) {
+            if (mLastJoystickXKeyCode != 0) {
+                deliverKeyEvent(new KeyEvent(time, time,
+                        KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+                mLastJoystickXKeyCode = 0;
+            }
+
+            mLastJoystickXDirection = xDirection;
+
+            if (xDirection != 0 && synthesizeNewKeys) {
+                mLastJoystickXKeyCode = xDirection > 0
+                        ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
+                deliverKeyEvent(new KeyEvent(time, time,
+                        KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+            }
+        }
+
+        if (yDirection != mLastJoystickYDirection) {
+            if (mLastJoystickYKeyCode != 0) {
+                deliverKeyEvent(new KeyEvent(time, time,
+                        KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+                mLastJoystickYKeyCode = 0;
+            }
+
+            mLastJoystickYDirection = yDirection;
+
+            if (yDirection != 0 && synthesizeNewKeys) {
+                mLastJoystickYKeyCode = yDirection > 0
+                        ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+                deliverKeyEvent(new KeyEvent(time, time,
+                        KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+            }
+        }
+    }
+
+    private static int joystickAxisValueToDirection(float value) {
+        if (value >= 0.5f) {
+            return 1;
+        } else if (value <= -0.5f) {
+            return -1;
+        } else {
+            return 0;
+        }
+    }
+
     /**
      * Returns true if the key is used for keyboard navigation.
      * @param keyEvent The key event.
@@ -3088,11 +3193,7 @@
         } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
             dispatchTrackball(event, sendDone);
         } else {
-            // TODO
-            Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
-            if (sendDone) {
-                finishInputEvent(false);
-            }
+            dispatchGenericMotion(event, sendDone);
         }
     }
 
@@ -3117,7 +3218,14 @@
         msg.arg1 = sendDone ? 1 : 0;
         sendMessageAtTime(msg, event.getEventTime());
     }
-    
+
+    private void dispatchGenericMotion(MotionEvent event, boolean sendDone) {
+        Message msg = obtainMessage(DISPATCH_GENERIC_MOTION);
+        msg.obj = event;
+        msg.arg1 = sendDone ? 1 : 0;
+        sendMessageAtTime(msg, event.getEventTime());
+    }
+
     public void dispatchAppVisibility(boolean visible) {
         Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
         msg.arg1 = visible ? 1 : 0;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 217e731..2095a93 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -187,6 +187,18 @@
         public boolean dispatchTrackballEvent(MotionEvent event);
 
         /**
+         * Called to process generic motion events.  At the very least your
+         * implementation must call
+         * {@link android.view.Window#superDispatchGenericMotionEvent} to do the
+         * standard processing.
+         *
+         * @param event The generic motion event.
+         *
+         * @return boolean Return true if this event was consumed.
+         */
+        public boolean dispatchGenericMotionEvent(MotionEvent event);
+
+        /**
          * Called to process population of {@link AccessibilityEvent}s.
          *
          * @param event The event.
@@ -1101,6 +1113,14 @@
     public abstract boolean superDispatchTrackballEvent(MotionEvent event);
     
     /**
+     * Used by custom windows, such as Dialog, to pass the generic motion event
+     * further down the view hierarchy. Application developers should
+     * not need to implement or call this.
+     *
+     */
+    public abstract boolean superDispatchGenericMotionEvent(MotionEvent event);
+
+    /**
      * Retrieve the top-level window decor view (containing the standard
      * window frame/decorations and the client's content inside of that), which
      * can be added as a window to the window manager.
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 6095a64..2a76e33 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -234,14 +234,6 @@
         // high time constant.
         private static final float MAX_DEVIATION_FROM_GRAVITY = 1.5f;
 
-        // Minimum acceleration considered, in m/s^2. Below this threshold sensor noise will have
-        // significant impact on the calculations and in case of the vector (0, 0, 0) there is no
-        // defined rotation or tilt at all. Low or zero readings can happen when space travelling
-        // or free falling, but more commonly when shaking or getting bad readings from the sensor.
-        // The accelerometer is turned off when not used and polling it too soon after it is
-        // turned on may result in (0, 0, 0).
-        private static final float MIN_ABS_ACCELERATION = 1.5f;
-
         // Actual sampling period corresponding to SensorManager.SENSOR_DELAY_NORMAL.  There's no
         // way to get this information from SensorManager.
         // Note the actual period is generally 3-30ms larger than this depending on the device, but
@@ -355,9 +347,6 @@
             float deviation = Math.abs(magnitude - SensorManager.STANDARD_GRAVITY);
 
             handleAccelerationDistrust(deviation);
-            if (magnitude < MIN_ABS_ACCELERATION) {
-                return; // Ignore tilt and orientation when (0, 0, 0) or low reading
-            }
 
             // only filter tilt when we're accelerating
             float alpha = 1;
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index a553a459..527a1869 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -45,6 +45,8 @@
  * are attached, as appropriate, to the request for revalidation of content. The
  * class also manages the cache size.
  *
+ * CacheManager may only be used if your activity contains a WebView.
+ *
  * @deprecated Access to the HTTP cache will be removed in a future release.
  */
 @Deprecated
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c635b39..d1e7e5c 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -124,6 +124,8 @@
 	android_media_ToneGenerator.cpp \
 	android_hardware_Camera.cpp \
 	android_hardware_SensorManager.cpp \
+	android_hardware_UsbDevice.cpp \
+	android_hardware_UsbRequest.cpp \
 	android_debug_JNITest.cpp \
 	android_util_FileObserver.cpp \
 	android/opengl/poly_clip.cpp.arm \
@@ -202,6 +204,7 @@
 	libwpa_client \
 	libjpeg \
 	libnfc_ndef \
+	libusbhost \
 
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 342b884..c1c6c91 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,6 +77,8 @@
 
 extern int register_android_hardware_Camera(JNIEnv *env);
 extern int register_android_hardware_SensorManager(JNIEnv *env);
+extern int register_android_hardware_UsbDevice(JNIEnv *env);
+extern int register_android_hardware_UsbRequest(JNIEnv *env);
 
 extern int register_android_media_AudioRecord(JNIEnv *env);
 extern int register_android_media_AudioSystem(JNIEnv *env);
@@ -1275,6 +1277,8 @@
     REG_JNI(register_com_android_internal_os_ZygoteInit),
     REG_JNI(register_android_hardware_Camera),
     REG_JNI(register_android_hardware_SensorManager),
+    REG_JNI(register_android_hardware_UsbDevice),
+    REG_JNI(register_android_hardware_UsbRequest),
     REG_JNI(register_android_media_AudioRecord),
     REG_JNI(register_android_media_AudioSystem),
     REG_JNI(register_android_media_AudioTrack),
diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp
new file mode 100644
index 0000000..22cf387
--- /dev/null
+++ b/core/jni/android_hardware_UsbDevice.cpp
@@ -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.
+ */
+
+#define LOG_TAG "UsbDeviceJNI"
+
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <usbhost/usbhost.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+using namespace android;
+
+static jfieldID field_context;
+
+struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice)
+{
+    return (struct usb_device*)env->GetIntField(javaDevice, field_context);
+}
+
+// in android_hardware_UsbEndpoint.cpp
+extern struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint);
+
+static jboolean
+android_hardware_UsbDevice_open(JNIEnv *env, jobject thiz, jstring deviceName,
+        jobject fileDescriptor)
+{
+    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
+    fd = dup(fd);
+    if (fd < 0)
+        return false;
+
+    const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+    struct usb_device* device = usb_device_new(deviceNameStr, fd);
+    if (device) {
+        env->SetIntField(thiz, field_context, (int)device);
+    } else {
+        LOGE("usb_device_open failed for %s", deviceNameStr);
+        close(fd);
+    }
+
+    env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+    return (device != NULL);
+}
+
+static void
+android_hardware_UsbDevice_close(JNIEnv *env, jobject thiz)
+{
+    LOGD("close\n");
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (device) {
+        usb_device_close(device);
+        env->SetIntField(thiz, field_context, 0);
+    }
+}
+
+static jint
+android_hardware_UsbDevice_get_fd(JNIEnv *env, jobject thiz)
+{
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (!device) {
+        LOGE("device is closed in native_get_fd");
+        return -1;
+    }
+    return usb_device_get_fd(device);
+}
+
+static jint
+android_hardware_UsbDevice_claim_interface(JNIEnv *env, jobject thiz, int interfaceID, jboolean force)
+{
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (!device) {
+        LOGE("device is closed in native_claim_interface");
+        return -1;
+    }
+
+    int ret = usb_device_claim_interface(device, interfaceID);
+    if (ret && force && errno == EBUSY) {
+        // disconnect kernel driver and try again
+        usb_device_connect_kernel_driver(device, interfaceID, false);
+        ret = usb_device_claim_interface(device, interfaceID);
+    }
+    return ret;
+}
+
+static jint
+android_hardware_UsbDevice_release_interface(JNIEnv *env, jobject thiz, int interfaceID)
+{
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (!device) {
+        LOGE("device is closed in native_release_interface");
+        return -1;
+    }
+    int ret = usb_device_release_interface(device, interfaceID);
+    if (ret == 0) {
+        // allow kernel to reconnect its driver
+        usb_device_connect_kernel_driver(device, interfaceID, true);
+    }
+    return ret;
+}
+
+static jint
+android_hardware_UsbDevice_control_request(JNIEnv *env, jobject thiz,
+        jint requestType, jint request, jint value, jint index,
+        jbyteArray buffer, jint length)
+{
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (!device) {
+        LOGE("device is closed in native_control_request");
+        return -1;
+    }
+
+    jbyte* bufferBytes = NULL;
+    if (buffer) {
+        if (env->GetArrayLength(buffer) < length) {
+            env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"), NULL);
+            return -1;
+        }
+        bufferBytes = env->GetByteArrayElements(buffer, 0);
+    }
+
+    jint result = usb_device_send_control(device, requestType, request,
+            value,  index, length, bufferBytes);
+
+    if (bufferBytes)
+        env->ReleaseByteArrayElements(buffer, bufferBytes, 0);
+
+    return result;
+}
+
+static jobject
+android_hardware_UsbDevice_request_wait(JNIEnv *env, jobject thiz)
+{
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (!device) {
+        LOGE("device is closed in native_request_wait");
+        return NULL;
+    }
+
+    struct usb_request* request = usb_request_wait(device);
+    if (request)
+        return (jobject)request->client_data;
+    else
+        return NULL;
+}
+
+static jstring
+android_hardware_UsbDevice_get_serial(JNIEnv *env, jobject thiz)
+{
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (!device) {
+        LOGE("device is closed in native_request_wait");
+        return NULL;
+    }
+    char* serial = usb_device_get_serial(device);
+    if (!serial)
+        return NULL;
+    jstring result = env->NewStringUTF(serial);
+    free(serial);
+    return result;
+}
+
+static jint
+android_hardware_UsbDevice_get_device_id(JNIEnv *env, jobject clazz, jstring name)
+{
+    const char *nameStr = env->GetStringUTFChars(name, NULL);
+    int id = usb_device_get_unique_id_from_name(nameStr);
+    env->ReleaseStringUTFChars(name, nameStr);
+    return id;
+}
+
+static jstring
+android_hardware_UsbDevice_get_device_name(JNIEnv *env, jobject clazz, jint id)
+{
+    char* name = usb_device_get_name_from_unique_id(id);
+    jstring result = env->NewStringUTF(name);
+    free(name);
+    return result;
+}
+
+static JNINativeMethod method_table[] = {
+    {"native_open",             "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z",
+                                        (void *)android_hardware_UsbDevice_open},
+    {"native_close",            "()V",  (void *)android_hardware_UsbDevice_close},
+    {"native_get_fd",           "()I",  (void *)android_hardware_UsbDevice_get_fd},
+    {"native_claim_interface",  "(IZ)Z",(void *)android_hardware_UsbDevice_claim_interface},
+    {"native_release_interface","(I)Z", (void *)android_hardware_UsbDevice_release_interface},
+    {"native_control_request",  "(IIII[BI)I",
+                                        (void *)android_hardware_UsbDevice_control_request},
+    {"native_request_wait",             "()Landroid/hardware/UsbRequest;",
+                                        (void *)android_hardware_UsbDevice_request_wait},
+    { "native_get_serial",      "()Ljava/lang/String;",
+                                        (void*)android_hardware_UsbDevice_get_serial },
+
+    // static methods
+    { "native_get_device_id", "(Ljava/lang/String;)I",
+                                        (void*)android_hardware_UsbDevice_get_device_id },
+    { "native_get_device_name", "(I)Ljava/lang/String;",
+                                        (void*)android_hardware_UsbDevice_get_device_name },
+};
+
+int register_android_hardware_UsbDevice(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("android/hardware/UsbDevice");
+    if (clazz == NULL) {
+        LOGE("Can't find android/hardware/UsbDevice");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find UsbDevice.mNativeContext");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbDevice",
+            method_table, NELEM(method_table));
+}
+
diff --git a/core/jni/android_hardware_UsbEndpoint.cpp b/core/jni/android_hardware_UsbEndpoint.cpp
new file mode 100644
index 0000000..00c8235
--- /dev/null
+++ b/core/jni/android_hardware_UsbEndpoint.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "UsbEndpoint"
+
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <usbhost/usbhost.h>
+
+#include <stdio.h>
+
+using namespace android;
+
+static jfieldID field_context;
+static jfieldID field_address;
+static jfieldID field_attributes;
+static jfieldID field_max_packet_size;
+static jfieldID field_interval;
+
+struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint)
+{
+    return (struct usb_endpoint*)env->GetIntField(javaEndpoint, field_context);
+}
+
+// in android_hardware_UsbDevice.cpp
+extern struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice);
+
+static jboolean
+android_hardware_UsbEndpoint_init(JNIEnv *env, jobject thiz, jobject javaDevice)
+{
+    LOGD("open\n");
+
+    struct usb_device* device = get_device_from_object(env, javaDevice);
+    if (!device) {
+        LOGE("device null in native_init");
+        return false;
+    }
+
+    // construct an endpoint descriptor from the Java object fields
+    struct usb_endpoint_descriptor desc;
+    desc.bLength = USB_DT_ENDPOINT_SIZE;
+    desc.bDescriptorType = USB_DT_ENDPOINT;
+    desc.bEndpointAddress = env->GetIntField(thiz, field_address);
+    desc.bmAttributes = env->GetIntField(thiz, field_attributes);
+    desc.wMaxPacketSize = env->GetIntField(thiz, field_max_packet_size);
+    desc.bInterval = env->GetIntField(thiz, field_interval);
+
+    struct usb_endpoint* endpoint = usb_endpoint_init(device, &desc);
+    if (endpoint)
+        env->SetIntField(thiz, field_context, (int)device);
+    return (endpoint != NULL);
+}
+
+static void
+android_hardware_UsbEndpoint_close(JNIEnv *env, jobject thiz)
+{
+    LOGD("close\n");
+    struct usb_endpoint* endpoint = get_endpoint_from_object(env, thiz);
+    if (endpoint) {
+        usb_endpoint_close(endpoint);
+        env->SetIntField(thiz, field_context, 0);
+    }
+}
+
+static JNINativeMethod method_table[] = {
+    {"native_init",             "(Landroid/hardware/UsbDevice;)Z",
+                                (void *)android_hardware_UsbEndpoint_init},
+    {"native_close",            "()V",  (void *)android_hardware_UsbEndpoint_close},
+};
+
+int register_android_hardware_UsbEndpoint(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("android/hardware/UsbEndpoint");
+    if (clazz == NULL) {
+        LOGE("Can't find android/hardware/UsbEndpoint");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find UsbEndpoint.mNativeContext");
+        return -1;
+    }
+    field_address = env->GetFieldID(clazz, "mAddress", "I");
+    if (field_address == NULL) {
+        LOGE("Can't find UsbEndpoint.mAddress");
+        return -1;
+    }
+    field_attributes = env->GetFieldID(clazz, "mAttributes", "I");
+    if (field_attributes == NULL) {
+        LOGE("Can't find UsbEndpoint.mAttributes");
+        return -1;
+    }
+    field_max_packet_size = env->GetFieldID(clazz, "mMaxPacketSize", "I");
+    if (field_max_packet_size == NULL) {
+        LOGE("Can't find UsbEndpoint.mMaxPacketSize");
+        return -1;
+    }
+    field_interval = env->GetFieldID(clazz, "mInterval", "I");
+    if (field_interval == NULL) {
+        LOGE("Can't find UsbEndpoint.mInterval");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbEndpoint",
+            method_table, NELEM(method_table));
+}
+
diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp
new file mode 100644
index 0000000..710afae
--- /dev/null
+++ b/core/jni/android_hardware_UsbRequest.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 "UsbRequestJNI"
+
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <usbhost/usbhost.h>
+
+#include <stdio.h>
+
+using namespace android;
+
+static jfieldID field_context;
+
+struct usb_request* get_request_from_object(JNIEnv* env, jobject java_request)
+{
+    return (struct usb_request*)env->GetIntField(java_request, field_context);
+}
+
+// in android_hardware_UsbDevice.cpp
+extern struct usb_device* get_device_from_object(JNIEnv* env, jobject java_device);
+
+static jboolean
+android_hardware_UsbRequest_init(JNIEnv *env, jobject thiz, jobject java_device,
+        jint ep_address, jint ep_attributes, jint ep_max_packet_size, jint ep_interval)
+{
+    LOGD("init\n");
+
+    struct usb_device* device = get_device_from_object(env, java_device);
+    if (!device) {
+        LOGE("device null in native_init");
+        return false;
+    }
+
+    // construct an endpoint descriptor from the Java object fields
+    struct usb_endpoint_descriptor desc;
+    desc.bLength = USB_DT_ENDPOINT_SIZE;
+    desc.bDescriptorType = USB_DT_ENDPOINT;
+    desc.bEndpointAddress = ep_address;
+    desc.bmAttributes = ep_attributes;
+    desc.wMaxPacketSize = ep_max_packet_size;
+    desc.bInterval = ep_interval;
+
+    struct usb_request* request = usb_request_new(device, &desc);
+    if (request)
+        env->SetIntField(thiz, field_context, (int)request);
+    return (request != NULL);
+}
+
+static void
+android_hardware_UsbRequest_close(JNIEnv *env, jobject thiz)
+{
+    LOGD("close\n");
+    struct usb_request* request = get_request_from_object(env, thiz);
+    if (request) {
+        usb_request_free(request);
+        env->SetIntField(thiz, field_context, 0);
+    }
+}
+
+static jboolean
+android_hardware_UsbRequest_queue_array(JNIEnv *env, jobject thiz,
+        jbyteArray buffer, jint length, jboolean out)
+{
+    struct usb_request* request = get_request_from_object(env, thiz);
+    if (!request) {
+        LOGE("request is closed in native_queue");
+        return false;
+    }
+
+    if (buffer && length) {
+        request->buffer = malloc(length);
+        if (!request->buffer)
+            return false;
+        if (out) {
+            // copy data from Java buffer to native buffer
+            env->GetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer);
+        }
+    } else {
+        request->buffer = NULL;
+    }
+    request->buffer_length = length;
+
+    if (usb_request_queue(request)) {
+        if (request->buffer) {
+            // free our buffer if usb_request_queue fails
+            free(request->buffer);
+            request->buffer = NULL;
+        }
+        return false;
+    } else {
+        // save a reference to ourselves so UsbDevice.waitRequest() can find us
+        request->client_data = (void *)env->NewGlobalRef(thiz);
+        return true;
+    }
+}
+
+static void
+android_hardware_UsbRequest_dequeue_array(JNIEnv *env, jobject thiz,
+        jbyteArray buffer, jint length, jboolean out)
+{
+    struct usb_request* request = get_request_from_object(env, thiz);
+    if (!request) {
+        LOGE("request is closed in native_dequeue");
+        return;
+    }
+
+    if (buffer && length && request->buffer && !out) {
+        // copy data from native buffer to Java buffer
+        env->SetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer);
+    }
+    free(request->buffer);
+    env->DeleteGlobalRef((jobject)request->client_data);
+
+}
+
+static jboolean
+android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz,
+        jobject buffer, jint length, jboolean out)
+{
+    struct usb_request* request = get_request_from_object(env, thiz);
+    if (!request) {
+        LOGE("request is closed in native_queue");
+        return false;
+    }
+
+    if (buffer && length) {
+        request->buffer = env->GetDirectBufferAddress(buffer);
+        if (!request->buffer)
+            return false;
+    } else {
+        request->buffer = NULL;
+    }
+    request->buffer_length = length;
+
+    if (usb_request_queue(request)) {
+        request->buffer = NULL;
+        return false;
+    } else {
+        // save a reference to ourselves so UsbDevice.waitRequest() can find us
+        // we also need this to make sure our native buffer is not deallocated
+        // while IO is active
+        request->client_data = (void *)env->NewGlobalRef(thiz);
+        return true;
+    }
+}
+
+static void
+android_hardware_UsbRequest_dequeue_direct(JNIEnv *env, jobject thiz)
+{
+    struct usb_request* request = get_request_from_object(env, thiz);
+    if (!request) {
+        LOGE("request is closed in native_dequeue");
+        return;
+    }
+    // all we need to do is delete our global ref
+    env->DeleteGlobalRef((jobject)request->client_data);
+}
+
+static jboolean
+android_hardware_UsbRequest_cancel(JNIEnv *env, jobject thiz)
+{
+    struct usb_request* request = get_request_from_object(env, thiz);
+    if (!request) {
+        LOGE("request is closed in native_cancel");
+        return false;
+    }
+    return (usb_request_cancel(request) == 0);
+}
+
+static JNINativeMethod method_table[] = {
+    {"native_init",             "(Landroid/hardware/UsbDevice;IIII)Z",
+                                            (void *)android_hardware_UsbRequest_init},
+    {"native_close",            "()V",      (void *)android_hardware_UsbRequest_close},
+    {"native_queue_array",      "([BIZ)Z",  (void *)android_hardware_UsbRequest_queue_array},
+    {"native_dequeue_array",    "([BIZ)V",  (void *)android_hardware_UsbRequest_dequeue_array},
+    {"native_queue_direct",     "(Ljava/nio/ByteBuffer;IZ)Z",
+                                            (void *)android_hardware_UsbRequest_queue_direct},
+    {"native_dequeue_direct",   "()V",      (void *)android_hardware_UsbRequest_dequeue_direct},
+    {"native_cancel",           "()Z",      (void *)android_hardware_UsbRequest_cancel},
+};
+
+int register_android_hardware_UsbRequest(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("android/hardware/UsbRequest");
+    if (clazz == NULL) {
+        LOGE("Can't find android/hardware/UsbRequest");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find UsbRequest.mNativeContext");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbRequest",
+            method_table, NELEM(method_table));
+}
+
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 351f264..f658479 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -30,7 +30,6 @@
 int ifc_get_default_route(const char *ifname);
 int ifc_remove_default_route(const char *ifname);
 int ifc_reset_connections(const char *ifname);
-int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2);
 
 int dhcp_do_request(const char *ifname,
                     in_addr_t *ipaddr,
@@ -193,24 +192,6 @@
     return env->NewStringUTF(::dhcp_get_errmsg());
 }
 
-static jboolean android_net_utils_configureInterface(JNIEnv* env,
-        jobject clazz,
-        jstring ifname,
-        jint ipaddr,
-        jint mask,
-        jint gateway,
-        jint dns1,
-        jint dns2)
-{
-    int result;
-    uint32_t lease;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_configure(nameStr, ipaddr, mask, gateway, dns1, dns2);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jboolean)(result == 0);
-}
-
 // ----------------------------------------------------------------------------
 
 /*
@@ -231,7 +212,6 @@
     { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z",  (void *)android_net_utils_runDhcp },
     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
-    { "configureNative", "(Ljava/lang/String;IIIII)Z",  (void *)android_net_utils_configureInterface },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
 };
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 25d3aca..34d8d29 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -85,6 +85,8 @@
     <protected-broadcast android:name="android.hardware.action.USB_CONNECTED" />
     <protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" />
     <protected-broadcast android:name="android.hardware.action.USB_STATE" />
+    <protected-broadcast android:name="android.hardware.action.USB_DEVICE_ATTACHED" />
+    <protected-broadcast android:name="android.hardware.action.USB_DEVICE_DETACHED" />
 
     <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
@@ -462,11 +464,10 @@
         android:label="@string/permlab_flashlight"
         android:description="@string/permdesc_flashlight" />
 
-    <!-- Allows an application to access USB devices
-         @hide -->
+    <!-- Allows an application to access USB devices -->
     <permission android:name="android.permission.ACCESS_USB"
         android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="normal"
         android:label="@string/permlab_accessUsb"
         android:description="@string/permdesc_accessUsb" />
 
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8a506a3..080d66d 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"تعيين الخادم الوكيل العمومي للجهاز لكي يتم استخدامه أثناء تمكين السياسة. يعين مشرف الجهاز الأول فقط الخادم الوكيل العمومي الفعال."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"تعيين انتهاء صلاحية كلمة المرور"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"التحكم في الوقت المستغرق قبل الحاجة إلى تغيير كلمة مرور شاشة التوقف"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"الرئيسية"</item>
     <item msgid="869923650527136615">"الجوال"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"تم توصيل تصحيح أخطاء USB"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"اختيار تعطيل تصحيح أخطاء USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"تحديد طريقة الإرسال"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789 أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"العناصر المرشحة"</u></string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 2736269..d1176b8 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Задаване на глобалния прокси сървър, който да се използва, когато правилото е активирано. Само първият администратор на устройството задава действителния глобален прокси сървър."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Срок на валидност на паролата"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Контролирайте след колко време трябва да се променя паролата при заключване на екрана"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Домашен"</item>
     <item msgid="869923650527136615">"Мобилен"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Отстраняването на грешки през USB е свързано"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Изберете, за да деактивирате отстраняването на грешки през USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Избиране на метод на въвеждане"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"кандидати"</u></string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index ba93303..e352fc0 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Defineix el servidor intermediari global del dispositiu que cal utilitzar mentre la política estigui activada. Només el primer administrador del dispositiu pot definir el servidor intermediari global efectiu."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Defineix la caducitat de la contrasenya"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controla quant de temps abans de la pantalla de bloqueig cal canviar la contrasenya"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Mòbil"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuració d\'USB connectada"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Seleccioneu-ho per desactivar la depuració d\'USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Selecció del mètode d\'entrada"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 7495a66..441ad01 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Vyberte globální proxy server, který se bude používat, když jsou zásady aktivní. Aktuální globální proxy server nastavuje pouze první správce zařízení."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Nastavit konec platnosti hesla"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ovládání doby, po jejímž uplynutí je nutné změnit heslo pro odemknutí obrazovky"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domů"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Ladění přes rozhraní USB připojeno"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Vyberte, chcete-li zakázat ladění USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Výběr metody zadávání dat"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" AÁBCČDĎEÉĚFGHCHIÍJKLMNŇOÓPQRŘSŠTŤUÚVWXYÝZŽ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AÁBCČDĎEÉĚFGHCHIÍJKLMNŇOÓPQRŘSŠTŤUÚVWXYÝZŽ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidáti"</u></string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0c9aefd..0242ee9 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angiv enhedens globale proxy, der skal bruges, mens politikken er aktiveret. Kun den første enhedsadministrator angiver den effektive globale proxy."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Indstil udløb for adgangskode"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontroller, hvor lang tid der skal gå, før adgangskoden til skærmlåsen skal ændres."</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Hjem"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-fejlretning er tilsluttet"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Vælg for at deaktivere USB-fejlretning."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Vælg indtastningsmetode"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 7856aaf..87f14c5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Den globalen Proxy des Geräts zur Verwendung während der Aktivierung der Richtlinie festlegen. Nur der erste Geräteadministrator kann den gültigen globalen Proxy festlegen."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Ablauf des Passworts festlegen"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Zeitraum bis zur Änderung des Passworts für die Bildschirmsperre festlegen"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Privat"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-Debugging verbunden"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Auswählen, um USB-Debugging zu deaktivieren."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Eingabemethode auswählen"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"Kandidaten"</u></string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 7dc9747..6b8db1e 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ορίστε τη χρήση του γενικού διακομιστή μεσολάβησης της συσκευής όταν είναι ενεργοποιημένη η πολιτική. Μόνο ο διαχειριστής της πρώτης συσκευής ορίζει τον ισχύοντα γενικό διακομιστή μεσολάβησης."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Ορισμός λήξης κωδ. πρόσβασης"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ελέγξτε πόσος χρόνος απομένει προτού πρέπει να αλλάξετε τον κωδικό πρόσβασης κλειδώματος της οθόνης"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Οικία"</item>
     <item msgid="869923650527136615">"Κινητό"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Συνδέθηκε ο εντοπισμός σφαλμάτων USB"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Επιλογή μεθόδου εισόδου"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"υποψήφιοι"</u></string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a63e2c4..a981ed6 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Set the device\'s global proxy to be used while policy is enabled. Only the first device admin sets the effective global proxy."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Set password expiry"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Control how long before lock-screen password needs to be changed"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Home"</item>
     <item msgid="869923650527136615">"Mobile"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Select to disable USB debugging."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Select input method"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidates"</u></string>
diff --git a/core/res/res/values-es-rUS-xlarge/strings.xml b/core/res/res/values-es-rUS-xlarge/strings.xml
index 81b3f1a..b1409ba 100644
--- a/core/res/res/values-es-rUS-xlarge/strings.xml
+++ b/core/res/res/values-es-rUS-xlarge/strings.xml
@@ -288,8 +288,6 @@
     <!-- XL -->
     <string name="capital_on" msgid="5705918046896729554">"ENCENDIDO"</string>
     <!-- XL -->
-    <string name="capital_off" msgid="6734525950925281617">"APAGADO"</string>
-    <!-- XL -->
     <string name="wait" msgid="8036803866051401072">"Espera"</string>
     <!-- XL -->
     <string name="heavy_weight_notification" msgid="5762367358298413602">"<xliff:g id="APP">%1$s</xliff:g> se está ejecutando"</string>
@@ -313,10 +311,6 @@
     <!-- XL -->
     <string name="permdesc_mediaStorageWrite" product="default" msgid="2372999661142345443">"Permite que una aplicación modifique los contenidos del almacenamiento interno de medios."</string>
     <!-- XL -->
-    <string name="policylab_encryptedStorage" msgid="488196329176602372">"Establecer la encriptación del almacenamiento"</string>
-    <!-- XL -->
-    <string name="policydesc_encryptedStorage" msgid="6111889605506443825">"Requiere que los datos almacenados de la aplicación estén encriptados"</string>
-    <!-- XL -->
     <string name="autofill_address_summary_name_format" msgid="7531610259426153850">"$1$2$3"</string>
     <!-- XL -->
     <string name="autofill_address_summary_format" msgid="8398158823767723887">"$1$2$3"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 5d1a25f..5e6645d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -489,10 +489,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configuración del proxy global de dispositivo que se utilizará mientras se habilita la política. Sólo la primera administración de dispositivo configura el proxy global efectivo."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Establecer la caducidad de la contraseña"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Verifica cuánto tiempo antes debes cambiar la contraseña de la pantalla de bloqueo"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Celular"</item>
@@ -813,8 +809,8 @@
     <string name="no" msgid="5141531044935541497">"Cancelar"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Atención"</string>
     <string name="loading" msgid="1760724998928255250">"Cargando..."</string>
-    <string name="capital_on" msgid="1544682755514494298">"Sí"</string>
-    <string name="capital_off" msgid="6815870386972805832">"No"</string>
+    <string name="capital_on" msgid="1544682755514494298">"Encendido"</string>
+    <string name="capital_off" msgid="6815870386972805832">"APAGADO"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Completar la acción mediante"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"Utilizar de manera predeterminada en esta acción."</string>
     <string name="clearDefaultHintMsg" msgid="4815455344600932173">"Borrar la predeterminación en Configuración de la página principal &gt; Aplicaciones &gt; Administrar aplicaciones."</string>
@@ -908,8 +904,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración de USB conectada"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Seleccionar para desactivar la depuración de USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Seleccionar método de entrada"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a4d6545..4f02c4b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el primer administrador de dispositivos define el servidor proxy global efectivo."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Definir caducidad contraseña"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Permite controlar cuándo se debe cambiar la contraseña de bloqueo de la pantalla."</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Móvil"</item>
@@ -505,7 +501,7 @@
     <item msgid="9192514806975898961">"Personalizar"</item>
   </string-array>
   <string-array name="emailAddressTypes">
-    <item msgid="8073994352956129127">"Casa"</item>
+    <item msgid="8073994352956129127">"Personal"</item>
     <item msgid="7084237356602625604">"Trabajo"</item>
     <item msgid="1112044410659011023">"Otra"</item>
     <item msgid="2374913952870110618">"Personalizar"</item>
@@ -814,8 +810,8 @@
     <string name="no" msgid="5141531044935541497">"Cancelar"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Atención"</string>
     <string name="loading" msgid="1760724998928255250">"Cargando…"</string>
-    <string name="capital_on" msgid="1544682755514494298">"SÍ"</string>
-    <string name="capital_off" msgid="6815870386972805832">"NO"</string>
+    <string name="capital_on" msgid="1544682755514494298">"Activado"</string>
+    <string name="capital_off" msgid="6815870386972805832">"Desconectado"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Completar acción utilizando"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"Utilizar de forma predeterminada para esta acción"</string>
     <string name="clearDefaultHintMsg" msgid="4815455344600932173">"Borrar valores predeterminados en la página de configuración de la pantalla de inicio del teléfono &gt; Aplicaciones &gt; Administrar aplicaciones\"."</string>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Dispositivo de depuración USB conectado"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Seleccionar para inhabilitar la depuración USB"</string>
     <string name="select_input_method" msgid="6865512749462072765">"Seleccionar método de introducción de texto"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 767655d..95871a4 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"پروکسی جهانی دستگاه مورد نظر را جهت استفاده هنگام فعال بودن خط مشی تنظیم کنید. فقط اولین سرپرست دستگاه پروکسی جهانی مفید را تنظیم می کند."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"تنظیم زمان انقضای رمز ورود"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"کنترل مدت زمانی که رمز ورود صفحه قفل قبل از تغییر یافتن لازم دارد"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"خانه"</item>
     <item msgid="869923650527136615">"تلفن همراه"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"رفع عیب USB متصل شد"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"انتخاب کنید تا رفع عیب USB غیرفعال شود."</string>
     <string name="select_input_method" msgid="6865512749462072765">"انتخاب روش ورودی"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"داوطلبین"</u></string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d8d13ef..1392ebb 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Aseta laitteen yleinen välityspalvelin käyttöön, kun käytäntö on käytössä. Vain ensimmäinen laitteen järjestelmänhallitsija voi asettaa käytettävän yleisen välityspalvelimen."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Aseta salasanan umpeutuminen"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Valitse, kuinka pian ruudunlukituksen poiston salasana tulee vaihtaa"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Puhelinnumero (koti)"</item>
     <item msgid="869923650527136615">"Mobiili"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-vianetsintä yhdistetty"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Poista USB-vianetsintä käytöstä valitsemalla tämä."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Valitse syöttötapa"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidaatit"</u></string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c3fb6eb..361d193 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Indiquez le proxy global à utiliser pour ce mobile lorsque les règles sont activées. Seul l\'administrateur principal du mobile peut définir le proxy global utilisé."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Définir date exp. mot de passe"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Définir la fréquence de changement du mot de passe de verrouillage d\'écran"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domicile"</item>
     <item msgid="869923650527136615">"Mobile"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB connecté"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Sélectionnez cette option pour désactiver le débogage USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Sélectionner un mode de saisie"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index fe9f319..befb526 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Postavi globalni proxy uređaja za upotrebu dok su pravila omogućena. Samo prvi administrator uređaja postavlja djelotvoran globalni proxy."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Postavi istek zaporke"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Nadzirite za koliko vremena zaporka za zaključani zaslon treba biti promijenjena"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Početna"</item>
     <item msgid="869923650527136615">"Mobilni"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Priključen je alat za uklanjanje programske pogreške USB-a"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Odaberite da biste onemogućili rješavanje programske pogreške na USB-u."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Odaberite način unosa"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidati"</u></string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index a090f0b..0ee1bbc 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Az eszköz globális proxyja lesz használatban, amíg az irányelv engedélyezve van. Csak az eszköz első rendszergazdája állíthatja be a tényleges globális proxyt."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Jelszó lejáratának beállítása"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Azt vezérli, mennyi időnként kell módosítani a képernyőt zároló jelszót"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Otthoni"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB hibakereső csatlakoztatva"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Válassza ezt az USB hibakeresés kikapcsolásához."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Válassza ki a beviteli módszert"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"jelöltek"</u></string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 8f738d6..304ef90 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Setel proxy global perangkat yang akandigunakan ketika kebijakan diaktifkan. Hanya admin perangkat pertama yang menyetel procy global yang berlaku."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Setel kedaluwarsa sandi"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontrol berapa lama sebelum sandi penguncian layar perlu diubah"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Rumah"</item>
     <item msgid="869923650527136615">"Seluler"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Debugging USB terhubung"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Pilih untuk menonaktifkan debugging USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Pilih metode masukan"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"calon"</u></string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 887a96d..282d375 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Imposta il proxy globale del dispositivo in modo da utilizzarlo mentre la norma è attiva. Il proxy globale effettivo è impostabile solo dal primo amministratore del dispositivo."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Imposta scadenza password"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Stabilisci la scadenza della password di blocco dello schermo"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Cellulare"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Debug USB collegato"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Seleziona per disattivare il debug USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Seleziona metodo di inserimento"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidati"</u></string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index eab8673..ea48002 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"הגדר את שרת proxy הגלובלי של ההתקן לשימוש כאשר המדיניות מופעלת. רק מנהל ההתקן הראשון מגדיר את שרת ה-proxy הגלובלי הפעיל."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"הגדר תפוגת תוקף של סיסמה"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"שלוט בפרק הזמן הדרוש לשינוי הסיסמה של נעילת המסך"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"דף הבית"</item>
     <item msgid="869923650527136615">"נייד"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"ניקוי באגים של USB מחובר"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"בחר כדי להשבית ניקוי באגים ב-USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"בחר שיטת קלט"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZאבגדהוזחטיכלמנסעפצקרשת"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ  0123456789אבגדהוזחטיכלמנסעפצקרשת"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"מועמדים"</u></string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index c1b9f5d..6df5f3d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"ポリシーが有効になっている場合は端末のグローバルプロキシが使用されるように設定します。有効なグローバルプロキシを設定できるのは最初のデバイス管理者だけです。"</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"パスワードの有効期限の設定"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"画面ロックパスワードの変更が必要になるまでの期間を指定します"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"自宅"</item>
     <item msgid="869923650527136615">"携帯"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USBデバッグが接続されました"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"USBデバッグを無効にする場合に選択します。"</string>
     <string name="select_input_method" msgid="6865512749462072765">"入力方法の選択"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"候補"</u></string>
@@ -975,7 +969,7 @@
     <string name="no_file_chosen" msgid="6363648562170759465">"ファイルが選択されていません"</string>
     <string name="reset" msgid="2448168080964209908">"リセット"</string>
     <string name="submit" msgid="1602335572089911941">"送信"</string>
-    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"運転モードになっています"</string>
+    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"運転モードを有効にする"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"運転モードを終了するには選択してください。"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"テザリングまたはアクセスポイントが有効です"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"タップして設定する"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index eda018d..10eba59 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"정책이 사용 설정되어 있는 동안 사용될 기기 전체 프록시를 설정합니다. 첫 번째 기기 관리자가 설정한 전체 프록시만 유효합니다."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"비밀번호 만료 설정"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"화면 잠금 비밀번호를 변경해야 하는 기간 변경"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"집"</item>
     <item msgid="869923650527136615">"모바일"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB 디버깅 연결됨"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"USB 디버깅을 사용하지 않으려면 선택합니다."</string>
     <string name="select_input_method" msgid="6865512749462072765">"입력 방법 선택"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"가능한 원인"</u></string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index f9f01e4..bbb629d 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Nustatyti įrenginio bendrąjį tarpinį serverį, kad būtų naudojamas, kol įgalinta politika. Tik pirmasis įrenginio administratorius nustato efektyvų bendrąjį tarpinį serverį."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Nust. slaptaž. galiojimo pab."</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Valdyti, per kiek laiko iki ekrano užrakinimo turi būti pakeistas slaptažodis"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Pagrindinis"</item>
     <item msgid="869923650527136615">"Mobilusis"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB derinimas prijungtas"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Pasirinkite, kas išjungtumėte USB derinimą."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Pasirinkti įvesties būdą"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidatai"</u></string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 6d25c54..7ef1e0a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Iestatiet izmantojamo ierīces globālo starpniekserveri, kad ir iespējota politika. Spēkā esošo globālo starpniekserveri iestata tikai pirmās ierīces administrators."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Paroles termiņa izb. iest."</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontrolē ekrāna bloķēšanas paroles maiņas intervālu"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Mājas"</item>
     <item msgid="869923650527136615">"Mobilais"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB atkļūdošana ir pievienota."</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Atlasiet, lai atspējotu USB atkļūdošanu."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Atlasiet ievades metodi"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidāti"</u></string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index aed9163..83fd2d3c 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angir den globale mellomtjeneren på enheten som skal brukes når regelen er aktivert. Kun den opprinnelige administratoren av enheten kan angi den globale mellomtjeneren."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Angi utløpsdato for passordet"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Velg hvor lenge det skal gå før passordet til låseskjermen må byttes"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Hjemmenummer"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-debugging tilkoblet"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Velg for å deaktivere USB-debugging."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Velg inndatametode"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string>
     <string name="candidates_style" msgid="4333913089637062257">"TAG_FONT"<u>"kandidater"</u>"CLOSE_FONT"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 062caee..3c03980 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Stel de algemene proxy voor het apparaat in die moet worden gebruikt terwijl het beleid is geactiveerd. Alleen de eerste apparaatbeheerder stelt de algemene proxy in."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Verval wachtwoord instellen"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Beheren hoe lang het duurt voordat het wachtwoord voor schermvergrendeling moet worden gewijzigd"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Thuis"</item>
     <item msgid="869923650527136615">"Mobiel"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-foutopsporing verbonden"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Selecteer deze optie om USB-foutopsporing uit te schakelen."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Invoermethode selecteren"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidaten"</u></string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 868f168..e82dbca 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ustaw globalny serwer proxy urządzenia do wykorzystywania przy włączonych zasadach. Tylko pierwszy administrator urządzenia ustawia obowiązujący globalny serwer proxy."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Ustaw wygasanie hasła"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontrola czasu, po którym należy zmienić hasło blokowania ekranu"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Dom"</item>
     <item msgid="869923650527136615">"Komórka"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Podłączono moduł debugowania USB"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Wybierz, aby wyłączyć debugowanie USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Wybierz metodę wprowadzania"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĄBCĆDEĘFGHIJKLŁMNŃOÓPQRSŚTUVWXYZŹŻ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandydaci"</u></string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index f306b8d..41634f8 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Definir o proxy global do aparelho a ser utilizado quando a política estiver activada. Só o primeiro administrador do aparelho define o proxy global efectivo."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Def. valid. da palavra-passe"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controle com que antecedência é necessário alterar a palavra-passe de bloqueio do ecrã"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Residência"</item>
     <item msgid="869923650527136615">"Móvel"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB ligada"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Seleccione para desactivar depuração USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Seleccionar método de entrada"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 7cd6cc4..572cbce 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configura o proxy global do dispositivo para ser usado enquanto a política estiver ativada. Somente o primeiro administrador do dispositivo pode configurar um verdadeiro proxy global."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Definir validade da senha"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controle quanto tempo uma senha de bloqueio de tela deve ficar ativa antes de ser alterada"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Residencial"</item>
     <item msgid="869923650527136615">"Celular"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Selecione para desativar a depuração USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Selecionar método de entrada"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index a3d8191..9a879c7 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -498,10 +498,6 @@
     <skip />
     <!-- no translation found for policydesc_expirePassword (7276906351852798814) -->
     <skip />
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Privat"</item>
     <item msgid="869923650527136615">"Telefonin"</item>
@@ -951,8 +947,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Debugging USB connectà"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Tscherner per deactivar il debugging USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Tscherner ina metoda d\'endataziun"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 07a6213..d0e1dc6 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Setaţi serverul proxy global pentru dispozitiv care să fie utilizat cât timp politica este activă. Numai primul administrator al dispozitivului poate seta serverul proxy global activ."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Setaţi expirarea parolei"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Controlarea duratei până când parola de blocare a ecranului trebuie modificată"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domiciliu"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depanarea USB este conectată"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Selectaţi pentru a dezactiva depanarea USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Selectaţi metoda de intrare"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidaţi"</u></string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index dfc5d08..22ee02a 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Настройте глобальный прокси-сервер устройства, который будет использоваться при активной политике. Глобальный прокси-сервер должен настроить первый администратор устройства."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Задать время действия пароля"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Задать время действия пароля перед появлением экрана блокировки"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Домашний"</item>
     <item msgid="869923650527136615">"Мобильный"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Отладка по USB разрешена"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Нажмите, чтобы отключить отладку USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Выберите способ ввода"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"варианты"</u></string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index e7bbaf6..397688d 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Vyberte globálny server proxy, ktorý sa bude používať po aktivácii pravidiel. Platný globálny server proxy nastavuje iba prvý správca zariadenia."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Nastav. koniec platnosti hesla"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ovládanie doby, po uplynutí ktorej treba zmeniť heslo na odomknutie obrazovky"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domovská stránka"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Ladenie cez rozhranie USB pripojené"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Vyberte, ak chcete zakázať ladenie USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Výber metódy vstupu"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" AÁÄBCČDĎDZDŽEÉFGHCHIÍJKLĽMNŇOÓÔPRŔSŠTŤUÚVWXYÝZŽ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidáti"</u></string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index c6b4302..91c42a8 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Nastavite globalni strežnik proxy naprave, ki bo v uporabi, ko je pravilnik omogočen. Samo skrbnik prve naprave lahko nastavi veljaven globalni strežnik proxy."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Nastavitev poteka gesla"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Nastavite, koliko časa prej je treba spremeniti geslo za odklepanje zaslona"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Začetna stran"</item>
     <item msgid="869923650527136615">"Mobilni"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"iskanje in odpravljanje napak USB je povezano"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Izberite, če želite onemogočiti iskanje in odpravljanje napak prek vrat USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Izbiranje načina vnosa"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidati"</u></string>
@@ -1003,7 +997,8 @@
     <string name="media_unknown_state" msgid="729192782197290385">"Neznano stanje zunanjih nosilcev podatkov."</string>
     <string name="share" msgid="1778686618230011964">"Deli z dr."</string>
     <string name="find" msgid="4808270900322985960">"Najdi"</string>
-    <string name="websearch" msgid="4337157977400211589">"Spletno iskanje"</string>
+    <!-- no translation found for websearch (4337157977400211589) -->
+    <skip />
     <!-- no translation found for gpsNotifTicker (5622683912616496172) -->
     <skip />
     <!-- no translation found for gpsNotifTitle (5446858717157416839) -->
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 0493685..c5c1e43 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Подесите глобални прокси сервер уређаја који ће се користити док су омогућене смернице. Само први администратор уређаја поставља ефективни глобални прокси сервер."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Подеси време истека лозинке"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Контролишите време када лозинка за закључавање екрана треба да се промени"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Кућа"</item>
     <item msgid="869923650527136615">"Мобилни"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Отклањање грешака са USB-а је успостављено"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Изаберите да бисте онемогућили отклањања грешака са USB-а."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Избор методе уноса"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"кандидати"</u></string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index a60e7f9..bda3863 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ange vilken global proxyserver som ska användas när policyn är aktiverad. Endast den första enhetsadministratören anger den faktiska globala proxyservern."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Ange lösenordets utgångsdatum"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Se hur långt det är kvar till du måste ändra lösenordet till låsningsskärmen"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Hem"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-felsökning ansluten"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Välj att inaktivera USB-felsökning."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Välj indatametod"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index f192c6d..41593f2 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"ตั้งค่าพร็อกซีส่วนกลางของอุปกรณ์ที่จะใช้ขณะเปิดการใช้งานนโยบาย เฉพาะผู้ดูแลอุปกรณ์คนแรกเท่านั้นที่ตั้งค่าพร็อกซีส่วนกลางที่มีผลบังคับ"</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"ตั้งค่าการหมดอายุของรหัสผ่าน"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"ควบคุมระยะเวลาก่อนที่จะต้องเปลี่ยนรหัสผ่านการล็อกหน้าจอ"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"บ้าน"</item>
     <item msgid="869923650527136615">"มือถือ"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"เชื่อมต่อการแก้ไขข้อบกพร่อง USB แล้ว"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"เลือกเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"</string>
     <string name="select_input_method" msgid="6865512749462072765">"เลือกวิธีป้อนข้อมูล"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"ตัวเลือก"</u></string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d42ef28..f65d77e 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Itakda ang pandaigdigang proxy ng device na gagamitin habang pinagana ang patakaran. Tanging ang unang admin ng device ang magtatakda sa may bisang pandaigdigang proxy."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Itakda pag-expire ng password"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kontrolin kung gaano katagal bago kailangang palitan ang password sa pag-lock ng screen"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Home"</item>
     <item msgid="869923650527136615">"Mobile"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Konektado ang debugging ng USB"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Piliin upang huwag paganahin ang debugging ng USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Pumili ng pamamaraan ng pag-input"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"mga kandidato"</u></string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 8b338dd..762ea47 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Politika etkin olduğunda kullanılacak cihaz genelinde geçerli proxy\'yi ayarlayın. Etkin genel proxy\'yi yalnızca ilk cihaz yöneticisi ayarlar."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Şifre süre sonu tarihi ayarla"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Ekran kilitleme şifresinin ne kadar süre sonra değiştirilmesi gerekeceğini denetleyin."</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Ev"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB hata ayıklaması bağlandı"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"USB hata ayıklamasını devre dışı bırakmak için tıklayın."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Giriş yöntemini seç"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"adaylar"</u></string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6e5fdaa..a2ad6dc 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Устан. використ. глоб. проксі, коли ввімкнено політику. Лише адміністратор першого пристрою встановлює активний глоб. проксі."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Установити термін дії пароля"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Регулює, за скільки часу перед блокуванням екрана треба змінювати пароль"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Дом."</item>
     <item msgid="869923650527136615">"Мобільний"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Налагодження USB підключ."</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Вибер., щоб вимкн. налагодж. USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Виберіть метод введ-ня"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"кандидати"</u></string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 08c8990..4e5220a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -487,10 +487,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Đặt proxy chung của điện thoại được sử dụng trong khi chính sách được bật. Chỉ quản trị viên đầu tiên của điện thoại mới có thể đặt proxy chung hiệu quả."</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"Đặt hết hạn mật khẩu"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"Kiểm soát thời lượng trước khi mật khẩu khóa màn hình cần được thay đổi"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Nhà riêng"</item>
     <item msgid="869923650527136615">"ĐT di động"</item>
@@ -904,8 +900,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Gỡ lỗi USB đã được kết nối"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"Chọn để vô hiệu hoá gỡ lỗi USB."</string>
     <string name="select_input_method" msgid="6865512749462072765">"Chọn phương thức nhập"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"ứng viên"</u></string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ecfdd63..1cfc5cf 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"请设置在启用政策的情况下要使用的设备全局代理。只有第一设备管理员才可设置有效的全局代理。"</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"设置密码有效期"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"控制屏幕锁定密码的使用期限"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"住宅"</item>
     <item msgid="869923650527136615">"手机"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"已连接 USB 调试"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"选择停用 USB 调试。"</string>
     <string name="select_input_method" msgid="6865512749462072765">"选择输入法"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"候选"</u></string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 28cb8b2..d0f460f 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -490,10 +490,6 @@
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"設定政策啟用時所要使用的裝置全域 Proxy,只有第一個裝置管理員所設定的全域 Proxy 具有效力。"</string>
     <string name="policylab_expirePassword" msgid="2314569545488269564">"設定密碼到期日"</string>
     <string name="policydesc_expirePassword" msgid="7276906351852798814">"控制螢幕鎖定密碼的使用期限"</string>
-    <!-- no translation found for policylab_encryptedStorage (8901326199909132915) -->
-    <skip />
-    <!-- no translation found for policydesc_encryptedStorage (2504984732631479399) -->
-    <skip />
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"住家電話"</item>
     <item msgid="869923650527136615">"行動電話"</item>
@@ -909,8 +905,6 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB 偵錯模式已啟用"</string>
     <string name="adb_active_notification_message" msgid="8470296818270110396">"選取以停用 USB 偵錯。"</string>
     <string name="select_input_method" msgid="6865512749462072765">"選取輸入方式"</string>
-    <!-- no translation found for configure_input_methods (6324843080254191535) -->
-    <skip />
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"待選項目"</u></string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e76692a..95fc271 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1330,6 +1330,22 @@
         <enum name="KEYCODE_PROG_YELLOW" value="185" />
         <enum name="KEYCODE_PROG_BLUE" value="186" />
         <enum name="KEYCODE_APP_SWITCH" value="187" />
+        <enum name="KEYCODE_BUTTON_1" value="188" />
+        <enum name="KEYCODE_BUTTON_2" value="189" />
+        <enum name="KEYCODE_BUTTON_3" value="190" />
+        <enum name="KEYCODE_BUTTON_4" value="191" />
+        <enum name="KEYCODE_BUTTON_5" value="192" />
+        <enum name="KEYCODE_BUTTON_6" value="193" />
+        <enum name="KEYCODE_BUTTON_7" value="194" />
+        <enum name="KEYCODE_BUTTON_8" value="195" />
+        <enum name="KEYCODE_BUTTON_9" value="196" />
+        <enum name="KEYCODE_BUTTON_10" value="197" />
+        <enum name="KEYCODE_BUTTON_11" value="198" />
+        <enum name="KEYCODE_BUTTON_12" value="199" />
+        <enum name="KEYCODE_BUTTON_13" value="200" />
+        <enum name="KEYCODE_BUTTON_14" value="201" />
+        <enum name="KEYCODE_BUTTON_15" value="202" />
+        <enum name="KEYCODE_BUTTON_16" value="203" />
     </attr>
 
     <!-- ***************************************************************** -->
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index ea91c21..df80546 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -58,10 +58,6 @@
         <group gid="sdcard_rw" />
     </permission>
 
-    <permission name="android.permission.ACCESS_USB" >
-        <group gid="usb" />
-    </permission>
-
     <permission name="android.permission.ACCESS_MTP" >
         <group gid="mtp" />
     </permission>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 1ee121e..0aefc31 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -262,6 +262,24 @@
 # key 240 "KEY_UNKNOWN"
 
 
+key 288   BUTTON_1
+key 289   BUTTON_2
+key 290   BUTTON_3
+key 291   BUTTON_4
+key 292   BUTTON_5
+key 293   BUTTON_6
+key 294   BUTTON_7
+key 295   BUTTON_8
+key 296   BUTTON_9
+key 297   BUTTON_10
+key 298   BUTTON_11
+key 299   BUTTON_12
+key 300   BUTTON_13
+key 301   BUTTON_14
+key 302   BUTTON_15
+key 303   BUTTON_16
+
+
 key 304   BUTTON_A
 key 305   BUTTON_B
 key 306   BUTTON_C
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 10ce347..32abcc0 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -467,37 +467,28 @@
           <li><a href="<?cs var:toroot ?>guide/developing/tools/mksdcard.html">mksdcard</a></li>
 
           <li><a href="/guide/developing/tools/monkey.html">Monkey</a></li>
-              <li class="toggle-list">
-                 <div>
-                     <a href="/guide/developing/tools/monkeyrunner_concepts.html">
-                     <span class="en">monkeyrunner</span>
-                  </a>
-                  </div>
-                  <ul>
-                      <li>
-                          <a href="/guide/developing/tools/MonkeyDevice.html">
-                                <span class="en">MonkeyDevice</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a href="/guide/developing/tools/MonkeyImage.html">
-                            <span class="en">MonkeyImage</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a href="/guide/developing/tools/MonkeyRunner.html">
-                            <span class="en">MonkeyRunner</span>
-                        </a>
-                    </li>
-                  </ul>
-              </li>
-              <li><a href="<?cs var:toroot ?>guide/developing/tools/proguard.html">ProGuard</a>
-<span class="new">new!</span></li>
-              <li><a href="<?cs var:toroot ?>guide/developing/tools/adb.html#sqlite">sqlite3</a></li>
-              <li><a href="<?cs var:toroot ?>guide/developing/tools/traceview.html" >Traceview</a></li>
-              <li><a href="<?cs var:toroot ?>guide/developing/tools/zipalign.html" >zipalign</a></li>
-          </ul>
-        </li>
+          <li class="toggle-list">
+            <div><a href="/guide/developing/tools/monkeyrunner_concepts.html">
+              <span class="en">monkeyrunner</span>
+            </a></div>
+            <ul>
+              <li><a href="/guide/developing/tools/MonkeyDevice.html">
+                <span class="en">MonkeyDevice</span>
+                </a></li>
+              <li><a href="/guide/developing/tools/MonkeyImage.html">
+                <span class="en">MonkeyImage</span>
+                </a></li>
+              <li><a href="/guide/developing/tools/MonkeyRunner.html">
+                <span class="en">MonkeyRunner</span>
+                </a></li>
+            </ul>
+          </li>
+          <li><a href="/guide/developing/tools/proguard.html">ProGuard</a></li>
+          <li><a href="/guide/developing/tools/adb.html#sqlite">sqlite3</a></li>
+          <li><a href="/guide/developing/tools/traceview.html">Traceview</a></li>
+          <li><a href="<?cs var:toroot ?>guide/developing/tools/zipalign.html">zipalign</a></li>
+        </ul>
+      </li>
     </ul>
   </li>
 
diff --git a/drm/common/Android.mk b/drm/common/Android.mk
index c79a91a..f1136c9 100644
--- a/drm/common/Android.mk
+++ b/drm/common/Android.mk
@@ -26,7 +26,6 @@
     DrmInfoStatus.cpp \
     DrmRights.cpp \
     DrmSupportInfo.cpp \
-    IDrmIOService.cpp \
     IDrmManagerService.cpp \
     IDrmServiceListener.cpp \
     DrmInfoEvent.cpp \
diff --git a/drm/common/IDrmIOService.cpp b/drm/common/IDrmIOService.cpp
deleted file mode 100644
index e44ca55..0000000
--- a/drm/common/IDrmIOService.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <drm/drm_framework_common.h>
-#include "IDrmIOService.h"
-
-using namespace android;
-
-void BpDrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) {
-    Parcel data, reply;
-
-    data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
-    data.writeString8(filePath);
-    data.writeString8(dataBuffer);
-
-    remote()->transact(WRITE_TO_FILE, data, &reply);
-}
-
-String8 BpDrmIOService::readFromFile(const String8& filePath) {
-
-    Parcel data, reply;
-
-    data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
-    data.writeString8(filePath);
-
-    remote()->transact(READ_FROM_FILE, data, &reply);
-    return reply.readString8();
-}
-
-IMPLEMENT_META_INTERFACE(DrmIOService, "drm.IDrmIOService");
-
-status_t BnDrmIOService::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-
-    switch (code) {
-    case WRITE_TO_FILE:
-    {
-        CHECK_INTERFACE(IDrmIOService, data, reply);
-
-        writeToFile(data.readString8(), data.readString8());
-        return DRM_NO_ERROR;
-    }
-
-    case READ_FROM_FILE:
-    {
-        CHECK_INTERFACE(IDrmIOService, data, reply);
-
-        String8 dataBuffer = readFromFile(data.readString8());
-        reply->writeString8(dataBuffer);
-        return DRM_NO_ERROR;
-    }
-
-    default:
-        return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
diff --git a/drm/drmioserver/Android.mk b/drm/drmioserver/Android.mk
deleted file mode 100644
index 11571c7..0000000
--- a/drm/drmioserver/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    main_drmioserver.cpp \
-    DrmIOService.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libutils \
-    libbinder
-
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
-
-LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon
-
-LOCAL_C_INCLUDES := \
-    $(TOP)/frameworks/base/drm/libdrmframework/include \
-    $(TOP)/frameworks/base/include
-
-LOCAL_MODULE:= drmioserver
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/drm/drmioserver/DrmIOService.cpp b/drm/drmioserver/DrmIOService.cpp
deleted file mode 100644
index 60e6e70..0000000
--- a/drm/drmioserver/DrmIOService.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "DrmIOService"
-#include <utils/Log.h>
-
-#include <binder/IServiceManager.h>
-#include "DrmIOService.h"
-#include "ReadWriteUtils.h"
-
-using namespace android;
-
-void DrmIOService::instantiate() {
-    LOGV("instantiate");
-    defaultServiceManager()->addService(String16("drm.drmIOService"), new DrmIOService());
-}
-
-DrmIOService::DrmIOService() {
-    LOGV("created");
-}
-
-DrmIOService::~DrmIOService() {
-    LOGV("Destroyed");
-}
-
-void DrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) {
-    LOGV("Entering writeToFile");
-    ReadWriteUtils::writeToFile(filePath, dataBuffer);
-}
-
-String8 DrmIOService::readFromFile(const String8& filePath) {
-    LOGV("Entering readFromFile");
-    return ReadWriteUtils::readBytes(filePath);
-}
-
diff --git a/drm/drmioserver/main_drmioserver.cpp b/drm/drmioserver/main_drmioserver.cpp
deleted file mode 100644
index 7ed048d6..0000000
--- a/drm/drmioserver/main_drmioserver.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <grp.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <utils/Log.h>
-#include <private/android_filesystem_config.h>
-
-#include <DrmIOService.h>
-
-using namespace android;
-
-int main(int argc, char** argv)
-{
-    sp<ProcessState> proc(ProcessState::self());
-    sp<IServiceManager> sm = defaultServiceManager();
-    LOGI("ServiceManager: %p", sm.get());
-    DrmIOService::instantiate();
-    ProcessState::self()->startThreadPool();
-    IPCThreadState::self()->joinThreadPool();
-}
-
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index b6e0c30..ef7d274 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -87,7 +87,7 @@
 }
 
 status_t DrmManager::loadPlugIns() {
-    String8 pluginDirPath("/system/lib/drm/plugins/native");
+    String8 pluginDirPath("/system/lib/drm");
     return loadPlugIns(pluginDirPath);
 }
 
diff --git a/drm/libdrmframework/include/DrmIOService.h b/drm/libdrmframework/include/DrmIOService.h
deleted file mode 100644
index 244124e..0000000
--- a/drm/libdrmframework/include/DrmIOService.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __DRM_IO_SERVICE_H__
-#define __DRM_IO_SERVICE_H__
-
-#include "IDrmIOService.h"
-
-namespace android {
-
-/**
- * This is the implementation class for DRM IO service.
- *
- * The instance of this class is created while starting the DRM IO service.
- *
- */
-class DrmIOService : public BnDrmIOService {
-public:
-    static void instantiate();
-
-private:
-    DrmIOService();
-    virtual ~DrmIOService();
-
-public:
-    void writeToFile(const String8& filePath, const String8& dataBuffer);
-    String8 readFromFile(const String8& filePath);
-};
-
-};
-
-#endif /* __DRM_IO_SERVICE_H__ */
-
diff --git a/drm/libdrmframework/include/IDrmIOService.h b/drm/libdrmframework/include/IDrmIOService.h
deleted file mode 100644
index 5e0d907..0000000
--- a/drm/libdrmframework/include/IDrmIOService.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __IDRM_IO_SERVICE_H__
-#define __IDRM_IO_SERVICE_H__
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/**
- * This is the interface class for DRM IO service.
- *
- */
-class IDrmIOService : public IInterface
-{
-public:
-    enum {
-        WRITE_TO_FILE = IBinder::FIRST_CALL_TRANSACTION,
-        READ_FROM_FILE
-    };
-
-public:
-    DECLARE_META_INTERFACE(DrmIOService);
-
-public:
-    /**
-     * Writes the data into the file path provided
-     *
-     * @param[in] filePath Path of the file
-     * @param[in] dataBuffer Data to write
-     */
-    virtual void writeToFile(const String8& filePath, const String8& dataBuffer) = 0;
-
-    /**
-     * Reads the data from the file path provided
-     *
-     * @param[in] filePath Path of the file
-     * @return Data read from the file
-     */
-    virtual String8 readFromFile(const String8& filePath) = 0;
-};
-
-/**
- * This is the Binder implementation class for DRM IO service.
- */
-class BpDrmIOService: public BpInterface<IDrmIOService>
-{
-public:
-    BpDrmIOService(const sp<IBinder>& impl)
-            : BpInterface<IDrmIOService>(impl) {}
-
-    virtual void writeToFile(const String8& filePath, const String8& dataBuffer);
-
-    virtual String8 readFromFile(const String8& filePath);
-};
-
-/**
- * This is the Binder implementation class for DRM IO service.
- */
-class BnDrmIOService: public BnInterface<IDrmIOService>
-{
-public:
-    virtual status_t onTransact(
-            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
-};
-
-};
-
-#endif /* __IDRM_IO_SERVICE_H__ */
-
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
index d4a6f18..af67aa3 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -60,7 +60,7 @@
     $(LOCAL_PATH)/include \
     external/openssl/include
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm/plugins/native
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index 9b1a897..dbccf29 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -212,6 +212,22 @@
     { "PROG_YELLOW", 185 },
     { "PROG_BLUE", 186 },
     { "APP_SWITCH", 187 },
+    { "BUTTON_1", 188 },
+    { "BUTTON_2", 189 },
+    { "BUTTON_3", 190 },
+    { "BUTTON_4", 191 },
+    { "BUTTON_5", 192 },
+    { "BUTTON_6", 193 },
+    { "BUTTON_7", 194 },
+    { "BUTTON_8", 195 },
+    { "BUTTON_9", 196 },
+    { "BUTTON_10", 197 },
+    { "BUTTON_11", 198 },
+    { "BUTTON_12", 199 },
+    { "BUTTON_13", 200 },
+    { "BUTTON_14", 201 },
+    { "BUTTON_15", 202 },
+    { "BUTTON_16", 203 },
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java
new file mode 100644
index 0000000..19ee92a
--- /dev/null
+++ b/media/java/android/mtp/MtpClient.java
@@ -0,0 +1,270 @@
+/*
+ * 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.mtp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.UsbConstants;
+import android.hardware.UsbDevice;
+import android.hardware.UsbInterface;
+import android.hardware.UsbManager;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class helps an application manage a list of connected MTP devices.
+ * It listens for MTP devices being attached and removed from the USB host bus
+ * and notifies the application when the MTP device list changes.
+ * {@hide}
+ */
+public class MtpClient {
+
+    private static final String TAG = "MtpClient";
+
+    private final Context mContext;
+    private final UsbManager mUsbManager;
+    private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
+    private final ArrayList<MtpDevice> mDeviceList = new ArrayList<MtpDevice>();
+
+    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String deviceName = intent.getStringExtra(UsbManager.EXTRA_DEVICE_NAME);
+
+            synchronized (mDeviceList) {
+                MtpDevice mtpDevice = getDeviceLocked(deviceName);
+
+                if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+                    if (mtpDevice == null) {
+                        UsbDevice usbDevice =
+                                (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                        mtpDevice = openDevice(usbDevice);
+                    }
+                    if (mtpDevice != null) {
+                        mDeviceList.add(mtpDevice);
+                        for (Listener listener : mListeners) {
+                            listener.deviceAdded(mtpDevice);
+                        }
+                    }
+                } else if (mtpDevice != null) {
+                    mDeviceList.remove(mtpDevice);
+                    for (Listener listener : mListeners) {
+                        listener.deviceRemoved(mtpDevice);
+                    }
+                }
+            }
+        }
+    };
+
+    public interface Listener {
+        public void deviceAdded(MtpDevice device);
+        public void deviceRemoved(MtpDevice device);
+    }
+
+   static public boolean isCamera(UsbDevice device) {
+        int count = device.getInterfaceCount();
+        for (int i = 0; i < count; i++) {
+            UsbInterface intf = device.getInterface(i);
+            if (intf.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
+                    intf.getInterfaceSubclass() == 1 &&
+                    intf.getInterfaceProtocol() == 1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private MtpDevice openDevice(UsbDevice usbDevice) {
+        if (isCamera(usbDevice)) {
+            MtpDevice mtpDevice = new MtpDevice(usbDevice);
+            if (mtpDevice.open(mUsbManager)) {
+                return mtpDevice;
+            }
+        }
+        return null;
+    }
+
+    public MtpClient(Context context) {
+        mContext = context;
+        mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        context.registerReceiver(mUsbReceiver, filter);
+
+        for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
+            MtpDevice mtpDevice = getDeviceLocked(usbDevice.getDeviceName());
+            if (mtpDevice == null) {
+                mtpDevice = openDevice(usbDevice);
+            }
+            if (mtpDevice != null) {
+                mDeviceList.add(mtpDevice);
+            }
+        }
+    }
+
+    public void close() {
+        mContext.unregisterReceiver(mUsbReceiver);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public void addListener(Listener listener) {
+        synchronized (mDeviceList) {
+            if (!mListeners.contains(listener)) {
+                mListeners.add(listener);
+            }
+        }
+    }
+
+    public void removeListener(Listener listener) {
+        synchronized (mDeviceList) {
+            mListeners.remove(listener);
+        }
+    }
+
+    public MtpDevice getDevice(String deviceName) {
+        synchronized (mDeviceList) {
+            return getDeviceLocked(deviceName);
+        }
+    }
+
+    public MtpDevice getDevice(int id) {
+        synchronized (mDeviceList) {
+            return getDeviceLocked(UsbDevice.getDeviceName(id));
+        }
+    }
+
+    private MtpDevice getDeviceLocked(String deviceName) {
+        for (MtpDevice device : mDeviceList) {
+            if (device.getDeviceName().equals(deviceName)) {
+                return device;
+            }
+        }
+        return null;
+    }
+
+    public List<MtpDevice> getDeviceList() {
+        synchronized (mDeviceList) {
+            return new ArrayList<MtpDevice>(mDeviceList);
+        }
+    }
+
+    public List<MtpStorageInfo> getStorageList(String deviceName) {
+        MtpDevice device = getDevice(deviceName);
+        if (device == null) {
+            return null;
+        }
+        int[] storageIds = device.getStorageIds();
+        if (storageIds == null) {
+            return null;
+        }
+
+        int length = storageIds.length;
+        ArrayList<MtpStorageInfo> storageList = new ArrayList<MtpStorageInfo>(length);
+        for (int i = 0; i < length; i++) {
+            MtpStorageInfo info = device.getStorageInfo(storageIds[i]);
+            if (info == null) {
+                Log.w(TAG, "getStorageInfo failed");
+            } else {
+                storageList.add(info);
+            }
+        }
+        return storageList;
+    }
+
+    public MtpObjectInfo getObjectInfo(String deviceName, int objectHandle) {
+        MtpDevice device = getDevice(deviceName);
+        if (device == null) {
+            return null;
+        }
+        return device.getObjectInfo(objectHandle);
+    }
+
+    public boolean deleteObject(String deviceName, int objectHandle) {
+        MtpDevice device = getDevice(deviceName);
+        if (device == null) {
+            return false;
+        }
+        return device.deleteObject(objectHandle);
+    }
+
+    public List<MtpObjectInfo> getObjectList(String deviceName, int storageId, int objectHandle) {
+        MtpDevice device = getDevice(deviceName);
+        if (device == null) {
+            return null;
+        }
+        if (objectHandle == 0) {
+            // all objects in root of storage
+            objectHandle = 0xFFFFFFFF;
+        }
+        int[] handles = device.getObjectHandles(storageId, 0, objectHandle);
+        if (handles == null) {
+            return null;
+        }
+
+        int length = handles.length;
+        ArrayList<MtpObjectInfo> objectList = new ArrayList<MtpObjectInfo>(length);
+        for (int i = 0; i < length; i++) {
+            MtpObjectInfo info = device.getObjectInfo(handles[i]);
+            if (info == null) {
+                Log.w(TAG, "getObjectInfo failed");
+            } else {
+                objectList.add(info);
+            }
+        }
+        return objectList;
+    }
+
+    public byte[] getObject(String deviceName, int objectHandle, int objectSize) {
+        MtpDevice device = getDevice(deviceName);
+        if (device == null) {
+            return null;
+        }
+        return device.getObject(objectHandle, objectSize);
+    }
+
+    public byte[] getThumbnail(String deviceName, int objectHandle) {
+        MtpDevice device = getDevice(deviceName);
+        if (device == null) {
+            return null;
+        }
+        return device.getThumbnail(objectHandle);
+    }
+
+    public boolean importFile(String deviceName, int objectHandle, String destPath) {
+        MtpDevice device = getDevice(deviceName);
+        if (device == null) {
+            return false;
+        }
+        return device.importFile(objectHandle, destPath);
+    }
+}
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
new file mode 100644
index 0000000..2d726c2
--- /dev/null
+++ b/media/java/android/mtp/MtpDevice.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mtp;
+
+import android.hardware.UsbDevice;
+import android.hardware.UsbManager;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * This class represents an MTP device connected on the USB host bus.
+ *
+ * {@hide}
+ */
+public final class MtpDevice {
+
+    private static final String TAG = "MtpDevice";
+
+    private final UsbDevice mDevice;
+
+    static {
+        System.loadLibrary("media_jni");
+    }
+
+    public MtpDevice(UsbDevice device) {
+        mDevice = device;
+    }
+
+    public boolean open(UsbManager manager) {
+        if (manager.openDevice(mDevice)) {
+            return native_open(mDevice.getDeviceName(), mDevice.getFileDescriptor());
+        } else {
+            return false;
+        }
+    }
+
+    public void close() {
+        Log.d(TAG, "close");
+        native_close();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        Log.d(TAG, "finalize");
+        try {
+            native_close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public String getDeviceName() {
+        return mDevice.getDeviceName();
+    }
+
+    public int getDeviceId() {
+        return mDevice.getDeviceId();
+    }
+
+    @Override
+    public String toString() {
+        return mDevice.getDeviceName();
+    }
+
+    public MtpDeviceInfo getDeviceInfo() {
+        return native_get_device_info();
+    }
+
+    public int[] getStorageIds() {
+        return native_get_storage_ids();
+    }
+
+    public int[] getObjectHandles(int storageId, int format, int objectHandle) {
+        return native_get_object_handles(storageId, format, objectHandle);
+    }
+
+    public byte[] getObject(int objectHandle, int objectSize) {
+        return native_get_object(objectHandle, objectSize);
+    }
+
+    public byte[] getThumbnail(int objectHandle) {
+        return native_get_thumbnail(objectHandle);
+    }
+
+    public MtpStorageInfo getStorageInfo(int storageId) {
+        return native_get_storage_info(storageId);
+    }
+
+    public MtpObjectInfo getObjectInfo(int objectHandle) {
+        return native_get_object_info(objectHandle);
+    }
+
+    public boolean deleteObject(int objectHandle) {
+        return native_delete_object(objectHandle);
+    }
+
+    public long getParent(int objectHandle) {
+        return native_get_parent(objectHandle);
+    }
+
+    public long getStorageID(int objectHandle) {
+        return native_get_storage_id(objectHandle);
+    }
+
+    // Reads a file from device to host to the specified destination.
+    // Returns true if the transfer succeeds.
+    public boolean importFile(int objectHandle, String destPath) {
+        return native_import_file(objectHandle, destPath);
+    }
+
+    // used by the JNI code
+    private int mNativeContext;
+
+    private native boolean native_open(String deviceName, int fd);
+    private native void native_close();
+    private native MtpDeviceInfo native_get_device_info();
+    private native int[] native_get_storage_ids();
+    private native MtpStorageInfo native_get_storage_info(int storageId);
+    private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
+    private native MtpObjectInfo native_get_object_info(int objectHandle);
+    private native byte[] native_get_object(int objectHandle, int objectSize);
+    private native byte[] native_get_thumbnail(int objectHandle);
+    private native boolean native_delete_object(int objectHandle);
+    private native long native_get_parent(int objectHandle);
+    private native long native_get_storage_id(int objectHandle);
+    private native boolean native_import_file(int objectHandle, String destPath);
+}
diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java
new file mode 100644
index 0000000..d918c20
--- /dev/null
+++ b/media/java/android/mtp/MtpDeviceInfo.java
@@ -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.
+ */
+
+package android.mtp;
+
+/**
+ * This class encapsulates information about an MTP device.
+ * This corresponds to the DeviceInfo Dataset described in
+ * section 5.1.1 of the MTP specification.
+ *
+ * {@hide}
+ */
+public class MtpDeviceInfo {
+
+    private String mManufacturer;
+    private String mModel;
+    private String mVersion;
+    private String mSerialNumber;
+
+    // only instantiated via JNI
+    private MtpDeviceInfo() {
+    }
+
+    /**
+     * Returns the manufacturer's name for the MTP device
+     *
+     * @return the manufacturer name
+     */
+    public final String getManufacturer() {
+        return mManufacturer;
+    }
+
+    /**
+     * Returns the model name for the MTP device
+     *
+     * @return the model name
+     */
+    public final String getModel() {
+        return mModel;
+    }
+
+    /**
+     * Returns the version string the MTP device
+     *
+     * @return the device version
+     */
+    public final String getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Returns the unique serial number for the MTP device
+     *
+     * @return the serial number
+     */
+    public final String getSerialNumber() {
+        return mSerialNumber;
+    }
+}
\ No newline at end of file
diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java
new file mode 100644
index 0000000..309d524
--- /dev/null
+++ b/media/java/android/mtp/MtpObjectInfo.java
@@ -0,0 +1,257 @@
+/*
+ * 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.mtp;
+
+/**
+ * This class encapsulates information about an object on an MTP device.
+ * This corresponds to the ObjectInfo Dataset described in
+ * section 5.3.1 of the MTP specification.
+ *
+ * {@hide}
+ */
+public final class MtpObjectInfo {
+    private int mHandle;
+    private int mStorageId;
+    private int mFormat;
+    private int mProtectionStatus;
+    private int mCompressedSize;
+    private int mThumbFormat;
+    private int mThumbCompressedSize;
+    private int mThumbPixWidth;
+    private int mThumbPixHeight;
+    private int mImagePixWidth;
+    private int mImagePixHeight;
+    private int mImagePixDepth;
+    private int mParent;
+    private int mAssociationType;
+    private int mAssociationDesc;
+    private int mSequenceNumber;
+    private String mName;
+    private long mDateCreated;
+    private long mDateModified;
+    private String mKeywords;
+
+    // only instantiated via JNI
+    private MtpObjectInfo() {
+    }
+
+    /**
+     * Returns the object handle for the MTP object
+     *
+     * @return the object handle
+     */
+    public final int getObjectHandle() {
+        return mHandle;
+    }
+
+    /**
+     * Returns the storage ID for the MTP object's storage unit
+     *
+     * @return the storage ID
+     */
+    public final int getStorageId() {
+        return mStorageId;
+    }
+
+    /**
+     * Returns the format code for the MTP object
+     *
+     * @return the format code
+     */
+    public final int getFormat() {
+        return mFormat;
+    }
+
+    /**
+     * Returns the protection status for the MTP object
+     * Possible values are:
+     *
+     * <ul>
+     * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NONE}
+     * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_READ_ONLY}
+     * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NON_TRANSFERABLE_DATA}
+     * </ul>
+     *
+     * @return the protection status
+     */
+    public final int getProtectionStatus() {
+        return mProtectionStatus;
+    }
+
+    /**
+     * Returns the size of the MTP object
+     *
+     * @return the object size
+     */
+    public final int getCompressedSize() {
+        return mCompressedSize;
+    }
+
+    /**
+     * Returns the format code for the MTP object's thumbnail
+     * Will be zero for objects with no thumbnail
+     *
+     * @return the thumbnail format code
+     */
+    public final int getThumbFormat() {
+        return mThumbFormat;
+    }
+
+    /**
+     * Returns the size of the MTP object's thumbnail
+     * Will be zero for objects with no thumbnail
+     *
+     * @return the thumbnail size
+     */
+    public final int getThumbCompressedSize() {
+        return mThumbCompressedSize;
+    }
+
+    /**
+     * Returns the width of the MTP object's thumbnail in pixels
+     * Will be zero for objects with no thumbnail
+     *
+     * @return the thumbnail width
+     */
+    public final int getThumbPixWidth() {
+        return mThumbPixWidth;
+    }
+
+    /**
+     * Returns the height of the MTP object's thumbnail in pixels
+     * Will be zero for objects with no thumbnail
+     *
+     * @return the thumbnail height
+     */
+    public final int getThumbPixHeight() {
+        return mThumbPixHeight;
+    }
+
+    /**
+     * Returns the width of the MTP object in pixels
+     * Will be zero for non-image objects
+     *
+     * @return the image width
+     */
+    public final int getImagePixWidth() {
+        return mImagePixWidth;
+    }
+
+    /**
+     * Returns the height of the MTP object in pixels
+     * Will be zero for non-image objects
+     *
+     * @return the image height
+     */
+    public final int getImagePixHeight() {
+        return mImagePixHeight;
+    }
+
+    /**
+     * Returns the depth of the MTP object in bits per pixel
+     * Will be zero for non-image objects
+     *
+     * @return the image depth
+     */
+    public final int getImagePixDepth() {
+        return mImagePixDepth;
+    }
+
+    /**
+     * Returns the object handle for the object's parent
+     * Will be zero for the root directory of a storage unit
+     *
+     * @return the object's parent
+     */
+    public final int getParent() {
+        return mParent;
+    }
+
+    /**
+     * Returns the association type for the MTP object
+     * Will be zero objects that are not of format
+     * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION}
+     * For directories the association type is typically
+     * {@link android.mtp.MtpConstants#ASSOCIATION_TYPE_GENERIC_FOLDER}
+     *
+     * @return the object's association type
+     */
+    public final int getAssociationType() {
+        return mAssociationType;
+    }
+
+    /**
+     * Returns the association description for the MTP object
+     * Will be zero objects that are not of format
+     * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION}
+     *
+     * @return the object's association description
+     */
+    public final int getAssociationDesc() {
+        return mAssociationDesc;
+    }
+
+   /**
+     * Returns the sequence number for the MTP object
+     * This field is typically not used for MTP devices,
+     * but is sometimes used to define a sequence of photos
+     * on PTP cameras.
+     *
+     * @return the object's sequence number
+     */
+    public final int getSequenceNumber() {
+        return mSequenceNumber;
+    }
+
+   /**
+     * Returns the name of the MTP object
+     *
+     * @return the object's name
+     */
+    public final String getName() {
+        return mName;
+    }
+
+   /**
+     * Returns the creation date of the MTP object
+     * The value is represented as milliseconds since January 1, 1970
+     *
+     * @return the object's creation date
+     */
+    public final long getDateCreated() {
+        return mDateCreated;
+    }
+
+   /**
+     * Returns the modification date of the MTP object
+     * The value is represented as milliseconds since January 1, 1970
+     *
+     * @return the object's modification date
+     */
+    public final long getDateModified() {
+        return mDateModified;
+    }
+
+   /**
+     * Returns a comma separated list of keywords for the MTP object
+     *
+     * @return the object's keyword list
+     */
+    public final String getKeywords() {
+        return mKeywords;
+    }
+}
diff --git a/media/java/android/mtp/MtpStorageInfo.java b/media/java/android/mtp/MtpStorageInfo.java
new file mode 100644
index 0000000..811455a
--- /dev/null
+++ b/media/java/android/mtp/MtpStorageInfo.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.
+ */
+
+package android.mtp;
+
+/**
+ * This class encapsulates information about a storage unit on an MTP device.
+ * This corresponds to the StorageInfo Dataset described in
+ * section 5.2.2 of the MTP specification.
+ *
+ * {@hide}
+ */
+public final class MtpStorageInfo {
+
+    private int mStorageId;
+    private long mMaxCapacity;
+    private long mFreeSpace;
+    private String mDescription;
+    private String mVolumeIdentifier;
+
+    // only instantiated via JNI
+    private MtpStorageInfo() {
+    }
+
+    /**
+     * Returns the storage ID for the storage unit
+     *
+     * @return the storage ID
+     */
+    public final int getStorageId() {
+        return mStorageId;
+    }
+
+    /**
+     * Returns the maximum storage capacity for the storage unit in bytes
+     *
+     * @return the maximum capacity
+     */
+    public final long getMaxCapacity() {
+        return mMaxCapacity;
+    }
+
+   /**
+     * Returns the amount of free space in the storage unit in bytes
+     *
+     * @return the amount of free space
+     */
+    public final long getFreeSpace() {
+        return mFreeSpace;
+    }
+
+   /**
+     * Returns the description string for the storage unit
+     *
+     * @return the storage unit description
+     */
+    public final String getDescription() {
+        return mDescription;
+    }
+
+   /**
+     * Returns the volume identifier for the storage unit
+     *
+     * @return the storage volume identifier
+     */
+    public final String getVolumeIdentifier() {
+        return mVolumeIdentifier;
+    }
+}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index ab6e512..2a89a2a 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -10,6 +10,7 @@
     android_media_MediaProfiles.cpp \
     android_media_AmrInputStream.cpp \
     android_mtp_MtpDatabase.cpp \
+    android_mtp_MtpDevice.cpp \
     android_mtp_MtpServer.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index f0609b2..0884e35 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -776,6 +776,7 @@
 extern int register_android_media_MediaProfiles(JNIEnv *env);
 extern int register_android_media_AmrInputStream(JNIEnv *env);
 extern int register_android_mtp_MtpDatabase(JNIEnv *env);
+extern int register_android_mtp_MtpDevice(JNIEnv *env);
 extern int register_android_mtp_MtpServer(JNIEnv *env);
 
 jint JNI_OnLoad(JavaVM* vm, void* reserved)
@@ -829,6 +830,11 @@
         goto bail;
     }
 
+    if (register_android_mtp_MtpDevice(env) < 0) {
+        LOGE("ERROR: MtpDevice native registration failed");
+        goto bail;
+    }
+
     if (register_android_mtp_MtpServer(env) < 0) {
         LOGE("ERROR: MtpServer native registration failed");
         goto bail;
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
new file mode 100644
index 0000000..9e67985
--- /dev/null
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -0,0 +1,663 @@
+/*
+ * 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 "MtpDeviceJNI"
+#include "utils/Log.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "private/android_filesystem_config.h"
+
+#include "MtpTypes.h"
+#include "MtpDevice.h"
+#include "MtpDeviceInfo.h"
+#include "MtpStorageInfo.h"
+#include "MtpObjectInfo.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static jfieldID field_context;
+
+jclass clazz_deviceInfo;
+jclass clazz_storageInfo;
+jclass clazz_objectInfo;
+
+jmethodID constructor_deviceInfo;
+jmethodID constructor_storageInfo;
+jmethodID constructor_objectInfo;
+
+// MtpDeviceInfo fields
+static jfieldID field_deviceInfo_manufacturer;
+static jfieldID field_deviceInfo_model;
+static jfieldID field_deviceInfo_version;
+static jfieldID field_deviceInfo_serialNumber;
+
+// MtpStorageInfo fields
+static jfieldID field_storageInfo_storageId;
+static jfieldID field_storageInfo_maxCapacity;
+static jfieldID field_storageInfo_freeSpace;
+static jfieldID field_storageInfo_description;
+static jfieldID field_storageInfo_volumeIdentifier;
+
+// MtpObjectInfo fields
+static jfieldID field_objectInfo_handle;
+static jfieldID field_objectInfo_storageId;
+static jfieldID field_objectInfo_format;
+static jfieldID field_objectInfo_protectionStatus;
+static jfieldID field_objectInfo_compressedSize;
+static jfieldID field_objectInfo_thumbFormat;
+static jfieldID field_objectInfo_thumbCompressedSize;
+static jfieldID field_objectInfo_thumbPixWidth;
+static jfieldID field_objectInfo_thumbPixHeight;
+static jfieldID field_objectInfo_imagePixWidth;
+static jfieldID field_objectInfo_imagePixHeight;
+static jfieldID field_objectInfo_imagePixDepth;
+static jfieldID field_objectInfo_parent;
+static jfieldID field_objectInfo_associationType;
+static jfieldID field_objectInfo_associationDesc;
+static jfieldID field_objectInfo_sequenceNumber;
+static jfieldID field_objectInfo_name;
+static jfieldID field_objectInfo_dateCreated;
+static jfieldID field_objectInfo_dateModified;
+static jfieldID field_objectInfo_keywords;
+
+#ifdef HAVE_ANDROID_OS
+
+MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
+{
+    return (MtpDevice*)env->GetIntField(javaDevice, field_context);
+}
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+#endif // HAVE_ANDROID_OS
+
+// ----------------------------------------------------------------------------
+
+static jboolean
+android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint fd)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("open\n");
+    const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+    MtpDevice* device = MtpDevice::open(deviceNameStr, fd);
+    env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+
+    if (device)
+        env->SetIntField(thiz, field_context, (int)device);
+    return (device != NULL);
+#endif
+}
+
+static void
+android_mtp_MtpDevice_close(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("close\n");
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (device) {
+        device->close();
+        delete device;
+        env->SetIntField(thiz, field_context, 0);
+    }
+#endif
+}
+
+static jobject
+android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (!device) {
+        LOGD("android_mtp_MtpDevice_get_device_info device is null");
+        return NULL;
+    }
+    MtpDeviceInfo* deviceInfo = device->getDeviceInfo();
+    if (!deviceInfo) {
+        LOGD("android_mtp_MtpDevice_get_device_info deviceInfo is null");
+        return NULL;
+    }
+    jobject info = env->NewObject(clazz_deviceInfo, constructor_deviceInfo);
+    if (info == NULL) {
+        LOGE("Could not create a MtpDeviceInfo object");
+        delete deviceInfo;
+        return NULL;
+    }
+
+    if (deviceInfo->mManufacturer)
+        env->SetObjectField(info, field_deviceInfo_manufacturer,
+            env->NewStringUTF(deviceInfo->mManufacturer));
+    if (deviceInfo->mModel)
+        env->SetObjectField(info, field_deviceInfo_model,
+            env->NewStringUTF(deviceInfo->mModel));
+    if (deviceInfo->mVersion)
+        env->SetObjectField(info, field_deviceInfo_version,
+            env->NewStringUTF(deviceInfo->mVersion));
+    if (deviceInfo->mSerial)
+        env->SetObjectField(info, field_deviceInfo_serialNumber,
+            env->NewStringUTF(deviceInfo->mSerial));
+
+    delete deviceInfo;
+    return info;
+#else
+    return NULL;
+#endif
+}
+
+static jintArray
+android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (!device)
+        return NULL;
+    MtpStorageIDList* storageIDs = device->getStorageIDs();
+    if (!storageIDs)
+        return NULL;
+
+    int length = storageIDs->size();
+    jintArray array = env->NewIntArray(length);
+    // FIXME is this cast safe?
+    env->SetIntArrayRegion(array, 0, length, (const jint *)storageIDs->array());
+
+    delete storageIDs;
+    return array;
+#else
+    return NULL;
+#endif
+}
+
+static jobject
+android_mtp_MtpDevice_get_storage_info(JNIEnv *env, jobject thiz, jint storageID)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (!device)
+        return NULL;
+    MtpStorageInfo* storageInfo = device->getStorageInfo(storageID);
+    if (!storageInfo)
+        return NULL;
+
+    jobject info = env->NewObject(clazz_storageInfo, constructor_storageInfo);
+    if (info == NULL) {
+        LOGE("Could not create a MtpStorageInfo object");
+        delete storageInfo;
+        return NULL;
+    }
+
+    if (storageInfo->mStorageID)
+        env->SetIntField(info, field_storageInfo_storageId, storageInfo->mStorageID);
+    if (storageInfo->mMaxCapacity)
+        env->SetLongField(info, field_storageInfo_maxCapacity, storageInfo->mMaxCapacity);
+    if (storageInfo->mFreeSpaceBytes)
+        env->SetLongField(info, field_storageInfo_freeSpace, storageInfo->mFreeSpaceBytes);
+    if (storageInfo->mStorageDescription)
+        env->SetObjectField(info, field_storageInfo_description,
+            env->NewStringUTF(storageInfo->mStorageDescription));
+    if (storageInfo->mVolumeIdentifier)
+        env->SetObjectField(info, field_storageInfo_volumeIdentifier,
+            env->NewStringUTF(storageInfo->mVolumeIdentifier));
+
+    delete storageInfo;
+    return info;
+#else
+    return NULL;
+#endif
+}
+
+static jintArray
+android_mtp_MtpDevice_get_object_handles(JNIEnv *env, jobject thiz,
+        jint storageID, jint format, jint objectID)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (!device)
+        return NULL;
+    MtpObjectHandleList* handles = device->getObjectHandles(storageID, format, objectID);
+    if (!handles)
+        return NULL;
+
+    int length = handles->size();
+    jintArray array = env->NewIntArray(length);
+    // FIXME is this cast safe?
+    env->SetIntArrayRegion(array, 0, length, (const jint *)handles->array());
+
+    delete handles;
+    return array;
+#else
+    return NULL;
+#endif
+}
+
+static jobject
+android_mtp_MtpDevice_get_object_info(JNIEnv *env, jobject thiz, jint objectID)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (!device)
+        return NULL;
+    MtpObjectInfo* objectInfo = device->getObjectInfo(objectID);
+    if (!objectInfo)
+        return NULL;
+    jobject info = env->NewObject(clazz_objectInfo, constructor_objectInfo);
+    if (info == NULL) {
+        LOGE("Could not create a MtpObjectInfo object");
+        delete objectInfo;
+        return NULL;
+    }
+
+    if (objectInfo->mHandle)
+        env->SetIntField(info, field_objectInfo_handle, objectInfo->mHandle);
+    if (objectInfo->mStorageID)
+        env->SetIntField(info, field_objectInfo_storageId, objectInfo->mStorageID);
+    if (objectInfo->mFormat)
+        env->SetIntField(info, field_objectInfo_format, objectInfo->mFormat);
+    if (objectInfo->mProtectionStatus)
+        env->SetIntField(info, field_objectInfo_protectionStatus, objectInfo->mProtectionStatus);
+    if (objectInfo->mCompressedSize)
+        env->SetIntField(info, field_objectInfo_compressedSize, objectInfo->mCompressedSize);
+    if (objectInfo->mThumbFormat)
+        env->SetIntField(info, field_objectInfo_thumbFormat, objectInfo->mThumbFormat);
+    if (objectInfo->mThumbCompressedSize)
+        env->SetIntField(info, field_objectInfo_thumbCompressedSize, objectInfo->mThumbCompressedSize);
+    if (objectInfo->mThumbPixWidth)
+        env->SetIntField(info, field_objectInfo_thumbPixWidth, objectInfo->mThumbPixWidth);
+    if (objectInfo->mThumbPixHeight)
+        env->SetIntField(info, field_objectInfo_thumbPixHeight, objectInfo->mThumbPixHeight);
+    if (objectInfo->mImagePixWidth)
+        env->SetIntField(info, field_objectInfo_imagePixWidth, objectInfo->mImagePixWidth);
+    if (objectInfo->mImagePixHeight)
+        env->SetIntField(info, field_objectInfo_imagePixHeight, objectInfo->mImagePixHeight);
+    if (objectInfo->mImagePixDepth)
+        env->SetIntField(info, field_objectInfo_imagePixDepth, objectInfo->mImagePixDepth);
+    if (objectInfo->mParent)
+        env->SetIntField(info, field_objectInfo_parent, objectInfo->mParent);
+    if (objectInfo->mAssociationType)
+        env->SetIntField(info, field_objectInfo_associationType, objectInfo->mAssociationType);
+    if (objectInfo->mAssociationDesc)
+        env->SetIntField(info, field_objectInfo_associationDesc, objectInfo->mAssociationDesc);
+    if (objectInfo->mSequenceNumber)
+        env->SetIntField(info, field_objectInfo_sequenceNumber, objectInfo->mSequenceNumber);
+    if (objectInfo->mName)
+        env->SetObjectField(info, field_objectInfo_name, env->NewStringUTF(objectInfo->mName));
+    if (objectInfo->mDateCreated)
+        env->SetLongField(info, field_objectInfo_dateCreated, objectInfo->mDateCreated);
+    if (objectInfo->mDateModified)
+        env->SetLongField(info, field_objectInfo_dateModified, objectInfo->mDateModified);
+    if (objectInfo->mKeywords)
+        env->SetObjectField(info, field_objectInfo_keywords,
+            env->NewStringUTF(objectInfo->mKeywords));
+
+    delete objectInfo;
+    return info;
+#else
+    return NULL;
+#endif
+}
+
+struct get_object_callback_data {
+    JNIEnv *env;
+    jbyteArray array;
+};
+
+static bool get_object_callback(void* data, int offset, int length, void* clientData)
+{
+    get_object_callback_data* cbData = (get_object_callback_data *)clientData;
+    cbData->env->SetByteArrayRegion(cbData->array, offset, length, (jbyte *)data);
+    return true;
+}
+
+static jbyteArray
+android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint objectSize)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (!device)
+        return NULL;
+
+    jbyteArray array = env->NewByteArray(objectSize);
+    if (!array) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return NULL;
+    }
+
+    get_object_callback_data data;
+    data.env = env;
+    data.array = array;
+
+    if (device->readObject(objectID, get_object_callback, objectSize, &data))
+        return array;
+#endif
+    return NULL;
+}
+
+static jbyteArray
+android_mtp_MtpDevice_get_thumbnail(JNIEnv *env, jobject thiz, jint objectID)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (!device)
+        return NULL;
+
+    int length;
+    void* thumbnail = device->getThumbnail(objectID, length);
+    if (! thumbnail)
+        return NULL;
+    jbyteArray array = env->NewByteArray(length);
+    env->SetByteArrayRegion(array, 0, length, (const jbyte *)thumbnail);
+
+    free(thumbnail);
+    return array;
+#else
+    return NULL;
+#endif
+}
+
+static jboolean
+android_mtp_MtpDevice_delete_object(JNIEnv *env, jobject thiz, jint object_id)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (device)
+        return device->deleteObject(object_id);
+    else
+ #endif
+        return NULL;
+}
+
+static jlong
+android_mtp_MtpDevice_get_parent(JNIEnv *env, jobject thiz, jint object_id)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (device)
+        return device->getParent(object_id);
+    else
+#endif
+        return -1;
+}
+
+static jlong
+android_mtp_MtpDevice_get_storage_id(JNIEnv *env, jobject thiz, jint object_id)
+{
+ #ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (device)
+        return device->getStorageID(object_id);
+    else
+#endif
+        return -1;
+}
+
+static jboolean
+android_mtp_MtpDevice_import_file(JNIEnv *env, jobject thiz, jint object_id, jstring dest_path)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (device) {
+        const char *destPathStr = env->GetStringUTFChars(dest_path, NULL);
+        bool result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664);
+        env->ReleaseStringUTFChars(dest_path, destPathStr);
+        return result;
+    }
+#endif
+    return NULL;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"native_open",             "(Ljava/lang/String;I)Z",
+                                        (void *)android_mtp_MtpDevice_open},
+    {"native_close",            "()V",  (void *)android_mtp_MtpDevice_close},
+    {"native_get_device_info",  "()Landroid/mtp/MtpDeviceInfo;",
+                                        (void *)android_mtp_MtpDevice_get_device_info},
+    {"native_get_storage_ids",  "()[I", (void *)android_mtp_MtpDevice_get_storage_ids},
+    {"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;",
+                                        (void *)android_mtp_MtpDevice_get_storage_info},
+    {"native_get_object_handles","(III)[I",
+                                        (void *)android_mtp_MtpDevice_get_object_handles},
+    {"native_get_object_info",  "(I)Landroid/mtp/MtpObjectInfo;",
+                                        (void *)android_mtp_MtpDevice_get_object_info},
+    {"native_get_object",       "(II)[B",(void *)android_mtp_MtpDevice_get_object},
+    {"native_get_thumbnail",    "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail},
+    {"native_delete_object",    "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
+    {"native_get_parent",       "(I)J", (void *)android_mtp_MtpDevice_get_parent},
+    {"native_get_storage_id",   "(I)J", (void *)android_mtp_MtpDevice_get_storage_id},
+    {"native_import_file",     "(ILjava/lang/String;)Z",
+                                        (void *)android_mtp_MtpDevice_import_file},
+};
+
+static const char* const kClassPathName = "android/mtp/MtpDevice";
+
+int register_android_mtp_MtpDevice(JNIEnv *env)
+{
+    jclass clazz;
+
+    LOGD("register_android_mtp_MtpDevice\n");
+
+    clazz = env->FindClass("android/mtp/MtpDeviceInfo");
+    if (clazz == NULL) {
+        LOGE("Can't find android/mtp/MtpDeviceInfo");
+        return -1;
+    }
+    constructor_deviceInfo = env->GetMethodID(clazz, "<init>", "()V");
+    if (constructor_deviceInfo == NULL) {
+        LOGE("Can't find android/mtp/MtpDeviceInfo constructor");
+        return -1;
+    }
+    field_deviceInfo_manufacturer = env->GetFieldID(clazz, "mManufacturer", "Ljava/lang/String;");
+    if (field_deviceInfo_manufacturer == NULL) {
+        LOGE("Can't find MtpDeviceInfo.mManufacturer");
+        return -1;
+    }
+    field_deviceInfo_model = env->GetFieldID(clazz, "mModel", "Ljava/lang/String;");
+    if (field_deviceInfo_model == NULL) {
+        LOGE("Can't find MtpDeviceInfo.mModel");
+        return -1;
+    }
+    field_deviceInfo_version = env->GetFieldID(clazz, "mVersion", "Ljava/lang/String;");
+    if (field_deviceInfo_version == NULL) {
+        LOGE("Can't find MtpDeviceInfo.mVersion");
+        return -1;
+    }
+    field_deviceInfo_serialNumber = env->GetFieldID(clazz, "mSerialNumber", "Ljava/lang/String;");
+    if (field_deviceInfo_serialNumber == NULL) {
+        LOGE("Can't find MtpDeviceInfo.mSerialNumber");
+        return -1;
+    }
+    clazz_deviceInfo = clazz;
+
+    clazz = env->FindClass("android/mtp/MtpStorageInfo");
+    if (clazz == NULL) {
+        LOGE("Can't find android/mtp/MtpStorageInfo");
+        return -1;
+    }
+    constructor_storageInfo = env->GetMethodID(clazz, "<init>", "()V");
+    if (constructor_storageInfo == NULL) {
+        LOGE("Can't find android/mtp/MtpStorageInfo constructor");
+        return -1;
+    }
+    field_storageInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
+    if (field_storageInfo_storageId == NULL) {
+        LOGE("Can't find MtpStorageInfo.mStorageId");
+        return -1;
+    }
+    field_storageInfo_maxCapacity = env->GetFieldID(clazz, "mMaxCapacity", "J");
+    if (field_storageInfo_maxCapacity == NULL) {
+        LOGE("Can't find MtpStorageInfo.mMaxCapacity");
+        return -1;
+    }
+    field_storageInfo_freeSpace = env->GetFieldID(clazz, "mFreeSpace", "J");
+    if (field_storageInfo_freeSpace == NULL) {
+        LOGE("Can't find MtpStorageInfo.mFreeSpace");
+        return -1;
+    }
+    field_storageInfo_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
+    if (field_storageInfo_description == NULL) {
+        LOGE("Can't find MtpStorageInfo.mDescription");
+        return -1;
+    }
+    field_storageInfo_volumeIdentifier = env->GetFieldID(clazz, "mVolumeIdentifier", "Ljava/lang/String;");
+    if (field_storageInfo_volumeIdentifier == NULL) {
+        LOGE("Can't find MtpStorageInfo.mVolumeIdentifier");
+        return -1;
+    }
+    clazz_storageInfo = clazz;
+
+    clazz = env->FindClass("android/mtp/MtpObjectInfo");
+    if (clazz == NULL) {
+        LOGE("Can't find android/mtp/MtpObjectInfo");
+        return -1;
+    }
+    constructor_objectInfo = env->GetMethodID(clazz, "<init>", "()V");
+    if (constructor_objectInfo == NULL) {
+        LOGE("Can't find android/mtp/MtpObjectInfo constructor");
+        return -1;
+    }
+    field_objectInfo_handle = env->GetFieldID(clazz, "mHandle", "I");
+    if (field_objectInfo_handle == NULL) {
+        LOGE("Can't find MtpObjectInfo.mHandle");
+        return -1;
+    }
+    field_objectInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
+    if (field_objectInfo_storageId == NULL) {
+        LOGE("Can't find MtpObjectInfo.mStorageId");
+        return -1;
+    }
+    field_objectInfo_format = env->GetFieldID(clazz, "mFormat", "I");
+    if (field_objectInfo_format == NULL) {
+        LOGE("Can't find MtpObjectInfo.mFormat");
+        return -1;
+    }
+    field_objectInfo_protectionStatus = env->GetFieldID(clazz, "mProtectionStatus", "I");
+    if (field_objectInfo_protectionStatus == NULL) {
+        LOGE("Can't find MtpObjectInfo.mProtectionStatus");
+        return -1;
+    }
+    field_objectInfo_compressedSize = env->GetFieldID(clazz, "mCompressedSize", "I");
+    if (field_objectInfo_compressedSize == NULL) {
+        LOGE("Can't find MtpObjectInfo.mCompressedSize");
+        return -1;
+    }
+    field_objectInfo_thumbFormat = env->GetFieldID(clazz, "mThumbFormat", "I");
+    if (field_objectInfo_thumbFormat == NULL) {
+        LOGE("Can't find MtpObjectInfo.mThumbFormat");
+        return -1;
+    }
+    field_objectInfo_thumbCompressedSize = env->GetFieldID(clazz, "mThumbCompressedSize", "I");
+    if (field_objectInfo_thumbCompressedSize == NULL) {
+        LOGE("Can't find MtpObjectInfo.mThumbCompressedSize");
+        return -1;
+    }
+    field_objectInfo_thumbPixWidth = env->GetFieldID(clazz, "mThumbPixWidth", "I");
+    if (field_objectInfo_thumbPixWidth == NULL) {
+        LOGE("Can't find MtpObjectInfo.mThumbPixWidth");
+        return -1;
+    }
+    field_objectInfo_thumbPixHeight = env->GetFieldID(clazz, "mThumbPixHeight", "I");
+    if (field_objectInfo_thumbPixHeight == NULL) {
+        LOGE("Can't find MtpObjectInfo.mThumbPixHeight");
+        return -1;
+    }
+    field_objectInfo_imagePixWidth = env->GetFieldID(clazz, "mImagePixWidth", "I");
+    if (field_objectInfo_imagePixWidth == NULL) {
+        LOGE("Can't find MtpObjectInfo.mImagePixWidth");
+        return -1;
+    }
+    field_objectInfo_imagePixHeight = env->GetFieldID(clazz, "mImagePixHeight", "I");
+    if (field_objectInfo_imagePixHeight == NULL) {
+        LOGE("Can't find MtpObjectInfo.mImagePixHeight");
+        return -1;
+    }
+    field_objectInfo_imagePixDepth = env->GetFieldID(clazz, "mImagePixDepth", "I");
+    if (field_objectInfo_imagePixDepth == NULL) {
+        LOGE("Can't find MtpObjectInfo.mImagePixDepth");
+        return -1;
+    }
+    field_objectInfo_parent = env->GetFieldID(clazz, "mParent", "I");
+    if (field_objectInfo_parent == NULL) {
+        LOGE("Can't find MtpObjectInfo.mParent");
+        return -1;
+    }
+    field_objectInfo_associationType = env->GetFieldID(clazz, "mAssociationType", "I");
+    if (field_objectInfo_associationType == NULL) {
+        LOGE("Can't find MtpObjectInfo.mAssociationType");
+        return -1;
+    }
+    field_objectInfo_associationDesc = env->GetFieldID(clazz, "mAssociationDesc", "I");
+    if (field_objectInfo_associationDesc == NULL) {
+        LOGE("Can't find MtpObjectInfo.mAssociationDesc");
+        return -1;
+    }
+    field_objectInfo_sequenceNumber = env->GetFieldID(clazz, "mSequenceNumber", "I");
+    if (field_objectInfo_sequenceNumber == NULL) {
+        LOGE("Can't find MtpObjectInfo.mSequenceNumber");
+        return -1;
+    }
+    field_objectInfo_name = env->GetFieldID(clazz, "mName", "Ljava/lang/String;");
+    if (field_objectInfo_name == NULL) {
+        LOGE("Can't find MtpObjectInfo.mName");
+        return -1;
+    }
+    field_objectInfo_dateCreated = env->GetFieldID(clazz, "mDateCreated", "J");
+    if (field_objectInfo_dateCreated == NULL) {
+        LOGE("Can't find MtpObjectInfo.mDateCreated");
+        return -1;
+    }
+    field_objectInfo_dateModified = env->GetFieldID(clazz, "mDateModified", "J");
+    if (field_objectInfo_dateModified == NULL) {
+        LOGE("Can't find MtpObjectInfo.mDateModified");
+        return -1;
+    }
+    field_objectInfo_keywords = env->GetFieldID(clazz, "mKeywords", "Ljava/lang/String;");
+    if (field_objectInfo_keywords == NULL) {
+        LOGE("Can't find MtpObjectInfo.mKeywords");
+        return -1;
+    }
+    clazz_objectInfo = clazz;
+
+    clazz = env->FindClass("android/mtp/MtpDevice");
+    if (clazz == NULL) {
+        LOGE("Can't find android/mtp/MtpDevice");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find MtpDevice.mNativeContext");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/mtp/MtpDevice", gMethods, NELEM(gMethods));
+}
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 70dc340..c25285e 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -21,7 +21,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                                       \
-                  MtpClient.cpp                         \
                   MtpDataPacket.cpp                     \
                   MtpDebug.cpp                          \
                   MtpDevice.cpp                         \
@@ -53,7 +52,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                                       \
-                  MtpClient.cpp                         \
                   MtpDataPacket.cpp                     \
                   MtpDebug.cpp                          \
                   MtpDevice.cpp                         \
diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp
deleted file mode 100644
index c830540..0000000
--- a/media/mtp/MtpClient.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MtpClient"
-
-#include "MtpDebug.h"
-#include "MtpClient.h"
-#include "MtpDevice.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <usbhost/usbhost.h>
-
-struct usb_device;
-
-namespace android {
-
-static bool isMtpDevice(uint16_t vendor, uint16_t product) {
-    // Sandisk Sansa Fuze
-    if (vendor == 0x0781 && product == 0x74c2)
-        return true;
-    // Samsung YP-Z5
-    if (vendor == 0x04e8 && product == 0x503c)
-        return true;
-    return false;
-}
-
-class MtpClientThread : public Thread {
-private:
-    MtpClient*   mClient;
-
-public:
-    MtpClientThread(MtpClient* client)
-        : mClient(client)
-    {
-    }
-
-    virtual bool threadLoop() {
-        return mClient->threadLoop();
-    }
-};
-
-
-MtpClient::MtpClient()
-    :   mThread(NULL),
-        mUsbHostContext(NULL),
-        mDone(false)
-{
-}
-
-MtpClient::~MtpClient() {
-    usb_host_cleanup(mUsbHostContext);
-}
-
-bool MtpClient::start() {
-    Mutex::Autolock autoLock(mMutex);
-
-    if (mThread)
-        return true;
-
-    mUsbHostContext = usb_host_init();
-    if (!mUsbHostContext)
-        return false;
-
-    mThread = new MtpClientThread(this);
-    mThread->run("MtpClientThread");
-    // wait for the thread to do initial device discovery before returning
-    mThreadStartCondition.wait(mMutex);
-
-    return true;
-}
-
-void MtpClient::stop() {
-    mDone = true;
-}
-
-MtpDevice* MtpClient::getDevice(int id) {
-    for (int i = 0; i < mDeviceList.size(); i++) {
-        MtpDevice* device = mDeviceList[i];
-        if (device->getID() == id)
-            return device;
-    }
-    return NULL;
-}
-
-bool MtpClient::usbDeviceAdded(const char *devname) {
-    struct usb_descriptor_header* desc;
-    struct usb_descriptor_iter iter;
-
-    struct usb_device *device = usb_device_open(devname);
-    if (!device) {
-        LOGE("usb_device_open failed\n");
-        return mDone;
-    }
-
-    usb_descriptor_iter_init(device, &iter);
-
-    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
-        if (desc->bDescriptorType == USB_DT_INTERFACE) {
-            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
-
-            if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
-                interface->bInterfaceSubClass == 1 && // Still Image Capture
-                interface->bInterfaceProtocol == 1)     // Picture Transfer Protocol (PIMA 15470)
-            {
-                LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
-                        usb_device_get_product_name(device));
-            } else if (interface->bInterfaceClass == 0xFF &&
-                    interface->bInterfaceSubClass == 0xFF &&
-                    interface->bInterfaceProtocol == 0) {
-                char* interfaceName = usb_device_get_string(device, interface->iInterface);
-                if (!interfaceName || strcmp(interfaceName, "MTP"))
-                    continue;
-                // Looks like an android style MTP device
-                LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
-                        usb_device_get_product_name(device));
-            } else {
-                // look for special cased devices based on vendor/product ID
-                // we are doing this mainly for testing purposes
-                uint16_t vendor = usb_device_get_vendor_id(device);
-                uint16_t product = usb_device_get_product_id(device);
-                if (!isMtpDevice(vendor, product)) {
-                    // not an MTP or PTP device
-                    continue;
-                }
-                // request MTP OS string and descriptor
-                // some music players need to see this before entering MTP mode.
-                char buffer[256];
-                memset(buffer, 0, sizeof(buffer));
-                int ret = usb_device_send_control(device,
-                        USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
-                        USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
-                        0, sizeof(buffer), buffer);
-                printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
-                if (ret > 0) {
-                    printf("got MTP string %s\n", buffer);
-                    ret = usb_device_send_control(device,
-                            USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
-                            0, 4, sizeof(buffer), buffer);
-                    printf("OS descriptor got %d\n", ret);
-                } else {
-                    printf("no MTP string\n");
-                }
-            }
-
-            // if we got here, then we have a likely MTP or PTP device
-
-            // interface should be followed by three endpoints
-            struct usb_endpoint_descriptor *ep;
-            struct usb_endpoint_descriptor *ep_in_desc = NULL;
-            struct usb_endpoint_descriptor *ep_out_desc = NULL;
-            struct usb_endpoint_descriptor *ep_intr_desc = NULL;
-            for (int i = 0; i < 3; i++) {
-                ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
-                if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
-                    LOGE("endpoints not found\n");
-                    return mDone;
-                }
-                if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
-                    if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-                        ep_in_desc = ep;
-                    else
-                        ep_out_desc = ep;
-                } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
-                    ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
-                    ep_intr_desc = ep;
-                }
-            }
-            if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
-                LOGE("endpoints not found\n");
-                return mDone;
-            }
-
-            if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
-                LOGE("usb_device_claim_interface failed errno: %d\n", errno);
-                return mDone;
-            }
-
-            MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
-                        ep_in_desc, ep_out_desc, ep_intr_desc);
-            mDeviceList.add(mtpDevice);
-            mtpDevice->initialize();
-            deviceAdded(mtpDevice);
-            return mDone;
-        }
-    }
-
-    usb_device_close(device);
-    return mDone;
-}
-
-bool MtpClient::usbDeviceRemoved(const char *devname) {
-    for (int i = 0; i < mDeviceList.size(); i++) {
-        MtpDevice* device = mDeviceList[i];
-        if (!strcmp(devname, device->getDeviceName())) {
-            deviceRemoved(device);
-            mDeviceList.removeAt(i);
-            delete device;
-            LOGD("Camera removed!\n");
-            break;
-        }
-    }
-    return mDone;
-}
-
-bool MtpClient::usbDiscoveryDone() {
-    Mutex::Autolock autoLock(mMutex);
-    mThreadStartCondition.signal();
-    return mDone;
-}
-
-bool MtpClient::threadLoop() {
-    usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this);
-    return false;
-}
-
-int MtpClient::usb_device_added(const char *devname, void* client_data) {
-    LOGD("usb_device_added %s\n", devname);
-    return ((MtpClient *)client_data)->usbDeviceAdded(devname);
-}
-
-int MtpClient::usb_device_removed(const char *devname, void* client_data) {
-    LOGD("usb_device_removed %s\n", devname);
-    return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
-}
-
-int MtpClient::usb_discovery_done(void* client_data) {
-    LOGD("usb_discovery_done\n");
-    return ((MtpClient *)client_data)->usbDiscoveryDone();
-}
-
-}  // namespace android
diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h
deleted file mode 100644
index fa5c527..0000000
--- a/media/mtp/MtpClient.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _MTP_CLIENT_H
-#define _MTP_CLIENT_H
-
-#include "MtpTypes.h"
-
-#include <utils/threads.h>
-
-struct usb_host_context;
-
-namespace android {
-
-class MtpClientThread;
-
-class MtpClient {
-private:
-    MtpDeviceList               mDeviceList;
-    MtpClientThread*            mThread;
-    Condition                   mThreadStartCondition;
-    Mutex                       mMutex;
-    struct usb_host_context*    mUsbHostContext;
-    bool                        mDone;
-
-public:
-                            MtpClient();
-    virtual                 ~MtpClient();
-
-    bool                    start();
-    void                    stop();
-
-    inline MtpDeviceList&   getDeviceList() { return mDeviceList; }
-    MtpDevice*              getDevice(int id);
-
-
-    virtual void            deviceAdded(MtpDevice *device) = 0;
-    virtual void            deviceRemoved(MtpDevice *device) = 0;
-
-private:
-    // these return true if we should stop monitoring USB and clean up
-    bool                    usbDeviceAdded(const char *devname);
-    bool                    usbDeviceRemoved(const char *devname);
-    bool                    usbDiscoveryDone();
-
-    friend class MtpClientThread;
-    bool                    threadLoop();
-    static int              usb_device_added(const char *devname, void* client_data);
-    static int              usb_device_removed(const char *devname, void* client_data);
-    static int              usb_discovery_done(void* client_data);
-};
-
-}; // namespace android
-
-#endif // _MTP_CLIENT_H
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index d22c72f..d02ed90 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -38,6 +38,121 @@
 
 namespace android {
 
+static bool isMtpDevice(uint16_t vendor, uint16_t product) {
+    // Sandisk Sansa Fuze
+    if (vendor == 0x0781 && product == 0x74c2)
+        return true;
+    // Samsung YP-Z5
+    if (vendor == 0x04e8 && product == 0x503c)
+        return true;
+    return false;
+}
+
+MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
+    struct usb_device *device = usb_device_new(deviceName, fd);
+    if (!device) {
+        LOGE("usb_device_new failed for %s", deviceName);
+        return NULL;
+    }
+
+    struct usb_descriptor_header* desc;
+    struct usb_descriptor_iter iter;
+
+    usb_descriptor_iter_init(device, &iter);
+
+    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+        if (desc->bDescriptorType == USB_DT_INTERFACE) {
+            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+            if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
+                interface->bInterfaceSubClass == 1 && // Still Image Capture
+                interface->bInterfaceProtocol == 1)     // Picture Transfer Protocol (PIMA 15470)
+            {
+                LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
+                        usb_device_get_product_name(device));
+            } else if (interface->bInterfaceClass == 0xFF &&
+                    interface->bInterfaceSubClass == 0xFF &&
+                    interface->bInterfaceProtocol == 0) {
+                char* interfaceName = usb_device_get_string(device, interface->iInterface);
+                if (!interfaceName || strcmp(interfaceName, "MTP"))
+                    continue;
+                // Looks like an android style MTP device
+                LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
+                        usb_device_get_product_name(device));
+            } else {
+                // look for special cased devices based on vendor/product ID
+                // we are doing this mainly for testing purposes
+                uint16_t vendor = usb_device_get_vendor_id(device);
+                uint16_t product = usb_device_get_product_id(device);
+                if (!isMtpDevice(vendor, product)) {
+                    // not an MTP or PTP device
+                    continue;
+                }
+                // request MTP OS string and descriptor
+                // some music players need to see this before entering MTP mode.
+                char buffer[256];
+                memset(buffer, 0, sizeof(buffer));
+                int ret = usb_device_send_control(device,
+                        USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
+                        USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
+                        0, sizeof(buffer), buffer);
+                printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
+                if (ret > 0) {
+                    printf("got MTP string %s\n", buffer);
+                    ret = usb_device_send_control(device,
+                            USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
+                            0, 4, sizeof(buffer), buffer);
+                    printf("OS descriptor got %d\n", ret);
+                } else {
+                    printf("no MTP string\n");
+                }
+            }
+
+            // if we got here, then we have a likely MTP or PTP device
+
+            // interface should be followed by three endpoints
+            struct usb_endpoint_descriptor *ep;
+            struct usb_endpoint_descriptor *ep_in_desc = NULL;
+            struct usb_endpoint_descriptor *ep_out_desc = NULL;
+            struct usb_endpoint_descriptor *ep_intr_desc = NULL;
+            for (int i = 0; i < 3; i++) {
+                ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
+                if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
+                    LOGE("endpoints not found\n");
+                    return NULL;
+                }
+                if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+                    if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+                        ep_in_desc = ep;
+                    else
+                        ep_out_desc = ep;
+                } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
+                    ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+                    ep_intr_desc = ep;
+                }
+            }
+            if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
+                LOGE("endpoints not found\n");
+                return NULL;
+            }
+
+            if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
+                LOGE("usb_device_claim_interface failed errno: %d\n", errno);
+                return NULL;
+            }
+
+            MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
+                        ep_in_desc, ep_out_desc, ep_intr_desc);
+            mtpDevice->initialize();
+            return mtpDevice;
+        }
+    }
+
+    usb_device_close(device);
+    LOGE("device not found");
+    return NULL;
+}
+
 MtpDevice::MtpDevice(struct usb_device* device, int interface,
             const struct usb_endpoint_descriptor *ep_in,
             const struct usb_endpoint_descriptor *ep_out,
@@ -49,7 +164,6 @@
         mRequestOut(NULL),
         mRequestIntr(NULL),
         mDeviceInfo(NULL),
-        mID(usb_device_get_unique_id(device)),
         mSessionID(0),
         mTransactionID(0),
         mReceivedResponse(false)
@@ -430,6 +544,98 @@
     return NULL;
 }
 
+bool MtpDevice::readObject(MtpObjectHandle handle,
+        bool (* callback)(void* data, int offset, int length, void* clientData),
+        int objectSize, void* clientData) {
+    Mutex::Autolock autoLock(mMutex);
+    bool result = false;
+
+    mRequest.reset();
+    mRequest.setParameter(1, handle);
+    if (sendRequest(MTP_OPERATION_GET_OBJECT)
+            && mData.readDataHeader(mRequestIn1)) {
+        uint32_t length = mData.getContainerLength();
+        if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
+            LOGE("readObject error objectSize: %d, length: %d",
+                    objectSize, length);
+            goto fail;
+        }
+        length -= MTP_CONTAINER_HEADER_SIZE;
+        uint32_t remaining = length;
+        int offset = 0;
+
+        int initialDataLength = 0;
+        void* initialData = mData.getData(initialDataLength);
+        if (initialData) {
+            if (initialDataLength > 0) {
+                if (!callback(initialData, 0, initialDataLength, clientData))
+                    goto fail;
+                remaining -= initialDataLength;
+                offset += initialDataLength;
+            }
+            free(initialData);
+        }
+
+        // USB reads greater than 16K don't work
+        char buffer1[16384], buffer2[16384];
+        mRequestIn1->buffer = buffer1;
+        mRequestIn2->buffer = buffer2;
+        struct usb_request* req = mRequestIn1;
+        void* writeBuffer = NULL;
+        int writeLength = 0;
+
+        while (remaining > 0 || writeBuffer) {
+            if (remaining > 0) {
+                // queue up a read request
+                req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+                if (mData.readDataAsync(req)) {
+                    LOGE("readDataAsync failed");
+                    goto fail;
+                }
+            } else {
+                req = NULL;
+            }
+
+            if (writeBuffer) {
+                // write previous buffer
+                if (!callback(writeBuffer, offset, writeLength, clientData)) {
+                    LOGE("write failed");
+                    // wait for pending read before failing
+                    if (req)
+                        mData.readDataWait(mDevice);
+                    goto fail;
+                }
+                offset += writeLength;
+                writeBuffer = NULL;
+            }
+
+            // wait for read to complete
+            if (req) {
+                int read = mData.readDataWait(mDevice);
+                if (read < 0)
+                    goto fail;
+
+                if (read > 0) {
+                    writeBuffer = req->buffer;
+                    writeLength = read;
+                    remaining -= read;
+                    req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+                } else {
+                    writeBuffer = NULL;
+                }
+            }
+        }
+
+        MtpResponseCode response = readResponse();
+        if (response == MTP_RESPONSE_OK)
+            result = true;
+    }
+
+fail:
+    return result;
+}
+
+
 // reads the object's data and writes it to the specified file path
 bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
     LOGD("readObject: %s", destPath);
@@ -507,10 +713,14 @@
                 if (read < 0)
                     goto fail;
 
-                writeBuffer = req->buffer;
-                writeLength = read;
-                remaining -= read;
-                req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+                if (read > 0) {
+                    writeBuffer = req->buffer;
+                    writeLength = read;
+                    remaining -= read;
+                    req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+                } else {
+                    writeBuffer = NULL;
+                }
             }
         }
 
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
index d0a0fb3..b69203e 100644
--- a/media/mtp/MtpDevice.h
+++ b/media/mtp/MtpDevice.h
@@ -45,9 +45,6 @@
     MtpDeviceInfo*          mDeviceInfo;
     MtpPropertyList         mDeviceProperties;
 
-    // a unique ID for the device
-    int                     mID;
-
     // current session ID
     MtpSessionID            mSessionID;
     // current transaction ID
@@ -67,9 +64,10 @@
                                     const struct usb_endpoint_descriptor *ep_in,
                                     const struct usb_endpoint_descriptor *ep_out,
                                     const struct usb_endpoint_descriptor *ep_intr);
-    virtual                 ~MtpDevice();
 
-    inline int              getID() const { return mID; }
+    static MtpDevice*       open(const char* deviceName, int fd);
+
+    virtual                 ~MtpDevice();
 
     void                    initialize();
     void                    close();
@@ -97,7 +95,11 @@
     MtpProperty*            getDevicePropDesc(MtpDeviceProperty code);
     MtpProperty*            getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format);
 
-    bool                   readObject(MtpObjectHandle handle, const char* destPath, int group,
+    bool                    readObject(MtpObjectHandle handle,
+                                    bool (* callback)(void* data, int offset,
+                                            int length, void* clientData),
+                                    int objectSize, void* clientData);
+    bool                    readObject(MtpObjectHandle handle, const char* destPath, int group,
                                     int perm);
 
 private:
diff --git a/media/tests/CameraBrowser/Android.mk b/media/tests/CameraBrowser/Android.mk
new file mode 100644
index 0000000..1d81129
--- /dev/null
+++ b/media/tests/CameraBrowser/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CameraBrowser
+
+include $(BUILD_PACKAGE)
diff --git a/media/tests/CameraBrowser/AndroidManifest.xml b/media/tests/CameraBrowser/AndroidManifest.xml
new file mode 100644
index 0000000..f167f4b
--- /dev/null
+++ b/media/tests/CameraBrowser/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.camerabrowser">
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_USB" />
+
+    <application android:label="@string/app_label"
+            android:name="com.android.camerabrowser.CameraBrowserApplication">
+
+        <activity android:name="CameraBrowser" android:label="Camera Browser">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="StorageBrowser" />
+        <activity android:name="ObjectBrowser" />
+        <activity android:name="ObjectViewer" />
+
+<!--
+        <receiver android:name="UsbReceiver">
+            <intent-filter>
+                <action android:name="android.hardware.action.USB_DEVICE_ATTACHED" />
+            </intent-filter>
+        </receiver>
+-->
+    </application>
+
+
+</manifest>
diff --git a/media/tests/CameraBrowser/res/layout/object_info.xml b/media/tests/CameraBrowser/res/layout/object_info.xml
new file mode 100644
index 0000000..a0499f2
--- /dev/null
+++ b/media/tests/CameraBrowser/res/layout/object_info.xml
@@ -0,0 +1,169 @@
+<?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.
+*/
+-->
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/object_info"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <TableRow>
+        <TextView android:id="@+id/name_label"
+            android:text="@string/name_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/name"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/size_label"
+            android:text="@string/size_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/size"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/format_label"
+            android:text="@string/format_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/format"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/thumb_width_label"
+            android:text="@string/thumb_width_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/thumb_width"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/thumb_height_label"
+            android:text="@string/thumb_height_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/thumb_height"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/thumb_size_label"
+            android:text="@string/thumb_size_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/thumb_size"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/width_label"
+            android:text="@string/width_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/width"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/height_label"
+            android:text="@string/height_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/height"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/depth_label"
+            android:text="@string/depth_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/depth"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/sequence_label"
+            android:text="@string/sequence_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/sequence"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/created_label"
+            android:text="@string/created_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/created"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/modified_label"
+            android:text="@string/modified_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/modified"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/keywords_label"
+            android:text="@string/keywords_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/keywords"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <ImageView android:id="@+id/thumbnail" />
+    </TableRow>
+    <TableRow>
+        <Button android:id="@+id/import_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/import_label">
+        </Button>
+        <Button android:id="@+id/delete_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/delete_label">
+        </Button>
+    </TableRow>
+</TableLayout>
+
diff --git a/media/tests/CameraBrowser/res/layout/object_list.xml b/media/tests/CameraBrowser/res/layout/object_list.xml
new file mode 100644
index 0000000..30c18bb
--- /dev/null
+++ b/media/tests/CameraBrowser/res/layout/object_list.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="fill_parent" 
+    android:layout_height="fill_parent">
+    
+    <ImageView android:id="@+id/thumbnail"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <TextView android:id="@+id/name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:gravity="center_vertical"
+        android:paddingLeft="6dip"
+        android:minHeight="?android:attr/listPreferredItemHeight" />
+</LinearLayout>
diff --git a/media/tests/CameraBrowser/res/values/strings.xml b/media/tests/CameraBrowser/res/values/strings.xml
new file mode 100644
index 0000000..932aaec
--- /dev/null
+++ b/media/tests/CameraBrowser/res/values/strings.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+
+<resources>
+    <string name="app_label">Camera Browser</string>
+
+    <!-- for object info -->
+    <string name="name_label">Name: </string>
+    <string name="size_label">Size: </string>
+    <string name="format_label">Format: </string>
+    <string name="thumb_width_label">Thumb Width: </string>
+    <string name="thumb_height_label">Thumb Height: </string>
+    <string name="thumb_size_label">Thumb Size: </string>
+    <string name="width_label">Width: </string>
+    <string name="height_label">Height: </string>
+    <string name="depth_label">Depth: </string>
+    <string name="sequence_label">Sequence: </string>
+    <string name="created_label">Created: </string>
+    <string name="modified_label">Modified: </string>
+    <string name="keywords_label">Keywords: </string>
+
+    <!-- button labels -->
+    <string name="import_label">Import</string>
+    <string name="delete_label">Delete</string>
+
+    <!-- toasts -->
+    <string name="object_saved_message">Object saved</string>
+    <string name="save_failed_message">Could not save object</string>
+    <string name="object_deleted_message">Object deleted</string>
+    <string name="delete_failed_message">Could not delete object</string>
+    <string name="start_activity_failed_message">Import succeeded, but could not display object</string>
+
+</resources>
diff --git a/media/tests/CameraBrowser/res/values/styles.xml b/media/tests/CameraBrowser/res/values/styles.xml
new file mode 100644
index 0000000..c869985
--- /dev/null
+++ b/media/tests/CameraBrowser/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<resources>
+    <style name="info_label">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:paddingRight">4dip</item>
+    </style>
+
+    <style name="info_value">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
+</resources>
+
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
new file mode 100644
index 0000000..f642d93
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.ListActivity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.mtp.MtpClient;
+import android.mtp.MtpDevice;
+import android.mtp.MtpDeviceInfo;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.TwoLineListItem;
+
+import java.util.List;
+
+ /**
+ * A list view displaying all connected cameras.
+ */
+public class CameraBrowser extends ListActivity implements MtpClient.Listener {
+
+    private static final String TAG = "CameraBrowser";
+
+    private MtpClient mClient;
+    private List<MtpDevice> mDeviceList;
+
+    private static final int MODEL_COLUMN = 0;
+    private static final int MANUFACTURER_COLUMN = 1;
+    private static final int COLUMN_COUNT = 2;
+
+    private class CameraAdapter extends BaseAdapter {
+        private final Context mContext;
+        private final LayoutInflater mInflater;
+
+        public CameraAdapter(Context c) {
+            mContext = c;
+            mInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        public int getCount() {
+            return mDeviceList.size();
+        }
+
+        public Object getItem(int position) {
+            return mDeviceList.get(position);
+        }
+
+        public long getItemId(int position) {
+            return position;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TwoLineListItem view;
+            if (convertView == null) {
+                view = (TwoLineListItem)mInflater.inflate(
+                        android.R.layout.simple_list_item_2, parent, false);
+            } else {
+                view = (TwoLineListItem)convertView;
+            }
+
+            TextView textView1 = (TextView)view.findViewById(com.android.internal.R.id.text1);
+            TextView textView2 = (TextView)view.findViewById(com.android.internal.R.id.text2);
+            MtpDevice device = mDeviceList.get(position);
+            MtpDeviceInfo info = device.getDeviceInfo();
+            if (info != null) {
+                textView1.setText(info.getManufacturer());
+                textView2.setText(info.getModel());
+            } else {
+                textView1.setText("???");
+                textView2.setText("???");
+            }
+            return view;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mClient = ((CameraBrowserApplication)getApplication()).getMtpClient();
+        mClient.addListener(this);
+        mDeviceList = mClient.getDeviceList();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        reload();
+    }
+
+    @Override
+    protected void onDestroy() {
+        mClient.removeListener(this);
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        Intent intent = new Intent(this, StorageBrowser.class);
+        intent.putExtra("device", mDeviceList.get(position).getDeviceName());
+        startActivity(intent);
+    }
+
+    private void reload() {
+        setListAdapter(new CameraAdapter(this));
+    }
+
+    public void deviceAdded(MtpDevice device) {
+        Log.d(TAG, "deviceAdded: " + device.getDeviceName());
+        mDeviceList = mClient.getDeviceList();
+        reload();
+    }
+
+    public void deviceRemoved(MtpDevice device) {
+        Log.d(TAG, "deviceRemoved: " + device.getDeviceName());
+        mDeviceList = mClient.getDeviceList();
+        reload();
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowserApplication.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowserApplication.java
new file mode 100644
index 0000000..6f1edfea
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowserApplication.java
@@ -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.android.camerabrowser;
+
+import android.app.Application;
+import android.mtp.MtpClient;
+
+
+public class CameraBrowserApplication extends Application {
+
+    private MtpClient mClient;
+
+    @Override
+    public void onCreate() {
+        mClient = new MtpClient(this);
+    }
+
+    @Override
+    public void onTerminate() {
+        mClient.close();
+        mClient = null;
+    }
+
+    public MtpClient getMtpClient() {
+        return mClient;
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java
new file mode 100644
index 0000000..736af1f
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.UsbManager;
+import android.util.Log;
+
+public class DeviceDisconnectedReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "DeviceDisconnectedReceiver";
+
+    private final Activity mActivity;
+    private final String mDeviceName;
+
+    public DeviceDisconnectedReceiver(Activity activity, String deviceName) {
+        mActivity = activity;
+        mDeviceName = deviceName;
+
+        IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        activity.registerReceiver(this, filter);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String deviceName = intent.getStringExtra(UsbManager.EXTRA_DEVICE_NAME);
+        Log.d(TAG, "ACTION_USB_DEVICE_DETACHED " + deviceName);
+
+        // close our activity if the device it is displaying is disconnected
+        if (deviceName.equals(mDeviceName)) {
+            mActivity.finish();
+        }
+    }
+}
\ No newline at end of file
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
new file mode 100644
index 0000000..82251d9
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.mtp.MtpClient;
+import android.mtp.MtpConstants;
+import android.mtp.MtpDevice;
+import android.mtp.MtpObjectInfo;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.List;
+
+ /**
+ * A list view displaying all objects within a container (folder or storage unit).
+ */
+public class ObjectBrowser extends ListActivity {
+
+    private static final String TAG = "ObjectBrowser";
+
+    private MtpClient mClient;
+    private List<MtpObjectInfo> mObjectList;
+    private String mDeviceName;
+    private int mStorageID;
+    private int mObjectID;
+    private DeviceDisconnectedReceiver mDisconnectedReceiver;
+
+    private class ObjectAdapter extends BaseAdapter {
+        private final Context mContext;
+        private final LayoutInflater mInflater;
+
+        public ObjectAdapter(Context c) {
+            mContext = c;
+            mInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        public int getCount() {
+            if (mObjectList == null) {
+                return 0;
+            } else {
+                return mObjectList.size();
+            }
+        }
+
+        public Object getItem(int position) {
+            return mObjectList.get(position);
+        }
+
+        public long getItemId(int position) {
+            return position;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View view;
+            if (convertView == null) {
+                view = mInflater.inflate(R.layout.object_list, parent, false);
+            } else {
+                view = convertView;
+            }
+
+            TextView nameView = (TextView)view.findViewById(R.id.name);
+            MtpObjectInfo info = mObjectList.get(position);
+            nameView.setText(info.getName());
+
+            int thumbFormat = info.getThumbFormat();
+            if (thumbFormat == MtpConstants.FORMAT_EXIF_JPEG
+                    || thumbFormat == MtpConstants.FORMAT_JFIF) {
+                byte[] thumbnail = mClient.getThumbnail(mDeviceName, info.getObjectHandle());
+                if (thumbnail != null) {
+                    Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
+                    if (bitmap != null) {
+                        ImageView thumbView = (ImageView)view.findViewById(R.id.thumbnail);
+                        thumbView.setImageBitmap(bitmap);
+                    }
+                }
+            }
+            return view;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mClient = ((CameraBrowserApplication)getApplication()).getMtpClient();
+        mDeviceName = getIntent().getStringExtra("device");
+        mStorageID = getIntent().getIntExtra("storage", 0);
+        mObjectID = getIntent().getIntExtra("object", 0);
+        mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceName);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mObjectList = mClient.getObjectList(mDeviceName, mStorageID, mObjectID);
+        setListAdapter(new ObjectAdapter(this));
+    }
+
+    @Override
+    protected void onDestroy() {
+        unregisterReceiver(mDisconnectedReceiver);
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        MtpObjectInfo info = mObjectList.get(position);
+        Intent intent;
+        if (info.getFormat() == MtpConstants.FORMAT_ASSOCIATION) {
+            intent = new Intent(this, ObjectBrowser.class);
+        } else {
+            intent = new Intent(this, ObjectViewer.class);
+        }
+        intent.putExtra("device", mDeviceName);
+        intent.putExtra("storage", mStorageID);
+        intent.putExtra("object", info.getObjectHandle());
+        startActivity(intent);
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
new file mode 100644
index 0000000..e9ea9f3
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.MediaScannerConnection;
+import android.media.MediaScannerConnection.MediaScannerConnectionClient;
+import android.mtp.MtpClient;
+import android.mtp.MtpConstants;
+import android.mtp.MtpObjectInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.util.Date;
+
+/**
+ * A view to display the properties of an object.
+ */
+public class ObjectViewer extends Activity implements View.OnClickListener {
+
+    private static final String TAG = "ObjectViewer";
+
+    private MtpClient mClient;
+    private String mDeviceName;
+    private int mStorageID;
+    private int mObjectID;
+    private String mFileName;
+    private Button mImportButton;
+    private Button mDeleteButton;
+    private DeviceDisconnectedReceiver mDisconnectedReceiver;
+
+    private final class ScannerClient implements MediaScannerConnectionClient {
+        private final Context mContext;
+        private String mPath;
+
+        public ScannerClient(Context context) {
+            mContext = context;
+        }
+
+        public void setScanPath(String path) {
+            mPath = path;
+        }
+
+        @Override
+        public void onMediaScannerConnected() {
+            mScannerConnection.scanFile(mPath, null);
+        }
+
+        @Override
+        public void onScanCompleted(String path, Uri uri) {
+            mScannerConnection.disconnect();
+
+            // try to start an activity to view the file
+            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+            try {
+                mContext.startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                Toast.makeText(mContext, R.string.start_activity_failed_message,
+                        Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
+
+    private MediaScannerConnection mScannerConnection;
+    private ScannerClient mScannerClient;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mClient = ((CameraBrowserApplication)getApplication()).getMtpClient();
+
+        setContentView(R.layout.object_info);
+
+        mImportButton = (Button)findViewById(R.id.import_button);
+        mImportButton.setOnClickListener(this);
+        mDeleteButton = (Button)findViewById(R.id.delete_button);
+        mDeleteButton.setOnClickListener(this);
+
+        mDeviceName = getIntent().getStringExtra("device");
+        mStorageID = getIntent().getIntExtra("storage", 0);
+        mObjectID = getIntent().getIntExtra("object", 0);
+        mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceName);
+        mScannerClient = new ScannerClient(this);
+        mScannerConnection = new MediaScannerConnection(this, mScannerClient);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        MtpObjectInfo info = mClient.getObjectInfo(mDeviceName, mObjectID);
+        if (info != null) {
+            TextView view = (TextView)findViewById(R.id.name);
+            mFileName = info.getName();
+            view.setText(mFileName);
+            view = (TextView)findViewById(R.id.format);
+            view.setText(Integer.toHexString(info.getFormat()).toUpperCase());
+            view = (TextView)findViewById(R.id.size);
+            view.setText(Long.toString(info.getCompressedSize()));
+            view = (TextView)findViewById(R.id.thumb_width);
+            view.setText(Long.toString(info.getThumbPixWidth()));
+            view = (TextView)findViewById(R.id.thumb_height);
+            view.setText(Long.toString(info.getThumbPixHeight()));
+            view = (TextView)findViewById(R.id.thumb_size);
+            view.setText(Long.toString(info.getThumbCompressedSize()));
+            view = (TextView)findViewById(R.id.width);
+            view.setText(Long.toString(info.getImagePixWidth()));
+            view = (TextView)findViewById(R.id.height);
+            view.setText(Long.toString(info.getImagePixHeight()));
+            view = (TextView)findViewById(R.id.depth);
+            view.setText(Long.toString(info.getImagePixDepth()));
+            view = (TextView)findViewById(R.id.sequence);
+            view.setText(Long.toString(info.getSequenceNumber()));
+            view = (TextView)findViewById(R.id.created);
+            Date date = new Date(info.getDateCreated() * 1000);
+            view.setText(date.toString());
+            view = (TextView)findViewById(R.id.modified);
+            date = new Date(info.getDateModified() * 1000);
+            view.setText(date.toString());
+            view = (TextView)findViewById(R.id.keywords);
+            view.setText(info.getKeywords());
+            int thumbFormat = info.getThumbFormat();
+            if (thumbFormat == MtpConstants.FORMAT_EXIF_JPEG
+                    || thumbFormat == MtpConstants.FORMAT_JFIF) {
+                byte[] thumbnail = mClient.getThumbnail(mDeviceName, info.getObjectHandle());
+                if (thumbnail != null) {
+                    Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
+                    if (bitmap != null) {
+                        ImageView thumbView = (ImageView)findViewById(R.id.thumbnail);
+                        thumbView.setImageBitmap(bitmap);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        unregisterReceiver(mDisconnectedReceiver);
+        super.onDestroy();
+    }
+
+    private void importObject() {
+        // copy file to /mnt/sdcard/imported/<filename>
+        File dest = Environment.getExternalStorageDirectory();
+        dest = new File(dest, "imported");
+        dest.mkdirs();
+        dest = new File(dest, mFileName);
+
+        if (mClient.importFile(mDeviceName, mObjectID, dest.getAbsolutePath())) {
+            Toast.makeText(this, R.string.object_saved_message, Toast.LENGTH_SHORT).show();
+
+            mScannerClient.setScanPath(dest.getAbsolutePath());
+            mScannerConnection.connect();
+        } else {
+            Toast.makeText(this, R.string.save_failed_message, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private void deleteObject() {
+        if (mClient.deleteObject(mDeviceName, mObjectID)) {
+            Toast.makeText(this, R.string.object_deleted_message, Toast.LENGTH_SHORT).show();
+            finish();
+        } else {
+            Toast.makeText(this, R.string.delete_failed_message, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    public void onClick(View v) {
+        if (v == mImportButton) {
+            importObject();
+        } else if (v == mDeleteButton) {
+            deleteObject();
+        }
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
new file mode 100644
index 0000000..7d5a5da
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.mtp.MtpClient;
+import android.mtp.MtpDevice;
+import android.mtp.MtpStorageInfo;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * A list view displaying all storage units on a device.
+ */
+public class StorageBrowser extends ListActivity {
+
+    private static final String TAG = "StorageBrowser";
+
+    private MtpClient mClient;
+    private String mDeviceName;
+    private List<MtpStorageInfo> mStorageList;
+    private DeviceDisconnectedReceiver mDisconnectedReceiver;
+
+    private class StorageAdapter extends BaseAdapter {
+        private final Context mContext;
+        private final LayoutInflater mInflater;
+
+        public StorageAdapter(Context c) {
+            mContext = c;
+            mInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        public int getCount() {
+            if (mStorageList == null) {
+                return 0;
+            } else {
+                return mStorageList.size();
+            }
+        }
+
+        public Object getItem(int position) {
+            return mStorageList.get(position);
+        }
+
+        public long getItemId(int position) {
+            return position;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView view;
+            if (convertView == null) {
+                view = (TextView)mInflater.inflate(
+                        android.R.layout.simple_list_item_1, parent, false);
+            } else {
+                view = (TextView)convertView;
+            }
+
+            MtpStorageInfo info = mStorageList.get(position);
+            if (info != null) {
+                view.setText(info.getDescription());
+            } else {
+                view.setText("???");
+            }
+            return view;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mClient = ((CameraBrowserApplication)getApplication()).getMtpClient();
+        mDeviceName = getIntent().getStringExtra("device");
+        mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceName);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mStorageList = mClient.getStorageList(mDeviceName);
+        setListAdapter(new StorageAdapter(this));
+    }
+
+    @Override
+    protected void onDestroy() {
+        unregisterReceiver(mDisconnectedReceiver);
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        Intent intent = new Intent(this, ObjectBrowser.class);
+        intent.putExtra("device", mDeviceName);
+        intent.putExtra("storage", mStorageList.get(position).getStorageId());
+        startActivity(intent);
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java
new file mode 100644
index 0000000..22d9443
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.hardware.UsbDevice;
+import android.hardware.UsbManager;
+import android.mtp.MtpClient;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+
+public class UsbReceiver extends BroadcastReceiver
+{
+    private static final String TAG = "UsbReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive " + intent);
+        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+            UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+            if (MtpClient.isCamera(device)) {
+                String deviceName = device.getDeviceName();
+                Log.d(TAG, "Got camera: " + deviceName);
+                intent = new Intent(context, StorageBrowser.class);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                intent.putExtra("device", deviceName);
+                context.startActivity(intent);
+            }
+        }
+    }
+}
diff --git a/native/include/android/input.h b/native/include/android/input.h
index e196686..bad363d 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -333,6 +333,7 @@
     AINPUT_SOURCE_CLASS_POINTER = 0x00000002,
     AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004,
     AINPUT_SOURCE_CLASS_POSITION = 0x00000008,
+    AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010,
 };
 
 enum {
@@ -340,10 +341,12 @@
 
     AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON,
     AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON,
+    AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON,
     AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
     AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER,
     AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
     AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
+    AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
 
     AINPUT_SOURCE_ANY = 0xffffff00,
 };
diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h
index b026a0c..c4a7eff 100644
--- a/native/include/android/keycodes.h
+++ b/native/include/android/keycodes.h
@@ -231,6 +231,22 @@
     AKEYCODE_PROG_YELLOW     = 185,
     AKEYCODE_PROG_BLUE       = 186,
     AKEYCODE_APP_SWITCH      = 187,
+    AKEYCODE_BUTTON_1        = 188,
+    AKEYCODE_BUTTON_2        = 189,
+    AKEYCODE_BUTTON_3        = 190,
+    AKEYCODE_BUTTON_4        = 191,
+    AKEYCODE_BUTTON_5        = 192,
+    AKEYCODE_BUTTON_6        = 193,
+    AKEYCODE_BUTTON_7        = 194,
+    AKEYCODE_BUTTON_8        = 195,
+    AKEYCODE_BUTTON_9        = 196,
+    AKEYCODE_BUTTON_10       = 197,
+    AKEYCODE_BUTTON_11       = 198,
+    AKEYCODE_BUTTON_12       = 199,
+    AKEYCODE_BUTTON_13       = 200,
+    AKEYCODE_BUTTON_14       = 201,
+    AKEYCODE_BUTTON_15       = 202,
+    AKEYCODE_BUTTON_16       = 203,
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b0eac55..5f01f4d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 53c44c6..aa98f7d 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 168d8af..583044d 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 1ff4a80..ea5694d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 969a7d8..b95c6cb 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index cc741ee..4043c2e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 83f29ff..39a462c 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 068451e..72373bb 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml b/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml
index 5c9c6fc..f29259a 100644
--- a/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml
@@ -9,8 +9,6 @@
     <string name="status_bar_settings_rotation_lock" msgid="9125161825884157545">"Bloquear orient. de pant."</string>
     <!-- XL -->
     <string name="recent_tasks_app_label" msgid="5550538721034982973">"Google Apps"</string>
-    <!-- XL -->
-    <string name="bluetooth_tethered" msgid="8017158699581472359">"Bluetooth anclado"</string>
     <!-- XL xlarge -->
     <string name="status_bar_settings_signal_meter_disconnected" msgid="4866302415753953027">"Sin conexión a Internet"</string>
     <!-- XL xlarge -->
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index da742e6..8013a19 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 58046ab..3acfb45 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 3c65051..0528d68 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index cbb2eec..1ce102a 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d199d9a..12308fb 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 3e60b8d..411336a 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index dbcd29d..b82aa77 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index db87cd3..db170ab 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9d751795..ca99d86 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 34eb41f..f2d5621 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 3b59844..25d8873 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 35d7503..6c5970a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a5848a9..90a5e44 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index cf81e85..fb33329 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 4d6140f..82405a8 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 339d146..4efdcbb 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index b50c422..07ef5ef 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 01544b5..aa14cce 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 35ffabb..17be76a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index 7b94e9b..e3fec88 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -57,6 +57,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 4e32019..2bb3c14 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 7a6a71e..89b9cc8 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 6b497b9..9716327 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 54145f2..b05b93c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -32,7 +32,8 @@
     <string name="invalid_charger" msgid="4549105996740522523">"Polnjenje po povezavi USB ni podprto."\n"Uporabite priloženi polnilnik."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Uporaba baterije"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Nastavitve"</string>
-    <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
+    <!-- no translation found for status_bar_settings_wifi_button (1733928151698311923) -->
+    <skip />
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Način za letalo"</string>
     <string name="status_bar_settings_rotation_lock" msgid="8361452930058000609">"Zakleni usmerjenost zaslona"</string>
     <string name="status_bar_settings_mute_label" msgid="554682549917429396">"TIHO"</string>
@@ -44,6 +45,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 6ee4009..ff1d3fb 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 99623c3..06b6880 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 7d154a6..2d337a90 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index bb56bc2..e94d3cc 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 361d7ee..4c3f818 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index ae40e56..85ead1c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 2ec07f1..71df0c3 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index cb15613..0cdc38d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 960130f..c85020f 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -44,6 +44,4 @@
     <skip />
     <!-- no translation found for bluetooth_tethered (7094101612161133267) -->
     <skip />
-    <!-- no translation found for status_bar_input_method_settings_configure_input_methods (737483394044014246) -->
-    <skip />
 </resources>
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 5e33f05..1f06dcc 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -221,7 +221,8 @@
         final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
 
         ab.setAdapter(mAdapter, this)
-                .setInverseBackgroundForced(true);
+                .setInverseBackgroundForced(true)
+                .setTitle(R.string.global_actions);
 
         final AlertDialog dialog = ab.create();
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
@@ -248,7 +249,6 @@
         } else {
             mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
         }
-        mDialog.setTitle(R.string.global_actions);
     }
 
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 6a1d199..76554a7 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1276,6 +1276,11 @@
         return mDecor.superDispatchTrackballEvent(event);
     }
 
+    @Override
+    public boolean superDispatchGenericMotionEvent(MotionEvent event) {
+        return mDecor.superDispatchGenericMotionEvent(event);
+    }
+
     /**
      * A key was pressed down and not handled by anything else in the window.
      *
@@ -1689,6 +1694,13 @@
                     .dispatchTrackballEvent(ev);
         }
 
+        @Override
+        public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+            final Callback cb = getCallback();
+            return cb != null && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) : super
+                    .dispatchGenericMotionEvent(ev);
+        }
+
         public boolean superDispatchKeyEvent(KeyEvent event) {
             return super.dispatchKeyEvent(event);
         }
@@ -1705,6 +1717,10 @@
             return super.dispatchTrackballEvent(event);
         }
 
+        public boolean superDispatchGenericMotionEvent(MotionEvent event) {
+            return super.dispatchGenericMotionEvent(event);
+        }
+
         @Override
         public boolean onTouchEvent(MotionEvent event) {
             return onInterceptTouchEvent(event);
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 487e73f..efdb177 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -624,7 +624,11 @@
         AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
         AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
         AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
-        AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
+        AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
+        AKEYCODE_BUTTON_1, AKEYCODE_BUTTON_2, AKEYCODE_BUTTON_3, AKEYCODE_BUTTON_4,
+        AKEYCODE_BUTTON_5, AKEYCODE_BUTTON_6, AKEYCODE_BUTTON_7, AKEYCODE_BUTTON_8,
+        AKEYCODE_BUTTON_9, AKEYCODE_BUTTON_10, AKEYCODE_BUTTON_11, AKEYCODE_BUTTON_12,
+        AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16,
 };
 
 int EventHub::openDevice(const char *devicePath) {
@@ -739,9 +743,9 @@
         //}
 
         // See if this is a keyboard.  Ignore everything in the button range except for
-        // gamepads which are also considered keyboards.
+        // joystick and gamepad buttons which are also considered keyboards.
         if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
-                || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
+                || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK),
                         sizeof_bit_array(BTN_DIGI))
                 || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
                         sizeof_bit_array(KEY_MAX + 1))) {
@@ -856,6 +860,18 @@
         }
     }
 
+    // See if this device is a joystick.
+    // Ignore touchscreens because they use the same absolute axes for other purposes.
+    if (device->classes & INPUT_DEVICE_CLASS_GAMEPAD
+            && !(device->classes & INPUT_DEVICE_CLASS_TOUCHSCREEN)) {
+        if (test_bit(ABS_X, abs_bitmask)
+                || test_bit(ABS_Y, abs_bitmask)
+                || test_bit(ABS_HAT0X, abs_bitmask)
+                || test_bit(ABS_HAT0Y, abs_bitmask)) {
+            device->classes |= INPUT_DEVICE_CLASS_JOYSTICK;
+        }
+    }
+
     // If the device isn't recognized as something we handle, don't monitor it.
     if (device->classes == 0) {
         LOGV("Dropping device: id=%d, path='%s', name='%s'",
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 74b7ec5..3b5a1ea 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -85,7 +85,7 @@
     int32_t flat;      // center flat position, eg. flat == 8 means center is between -8 and 8
     int32_t fuzz;      // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
 
-    inline int32_t getRange() { return maxValue - minValue; }
+    inline int32_t getRange() const { return maxValue - minValue; }
 
     inline void clear() {
         valid = false;
@@ -100,7 +100,7 @@
  * Input device classes.
  */
 enum {
-    /* The input device is a keyboard. */
+    /* The input device is a keyboard or has buttons. */
     INPUT_DEVICE_CLASS_KEYBOARD      = 0x00000001,
 
     /* The input device is an alpha-numeric keyboard (not just a dial pad). */
@@ -123,6 +123,9 @@
 
     /* The input device has switches. */
     INPUT_DEVICE_CLASS_SWITCH        = 0x00000080,
+
+    /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
+    INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,
 };
 
 /*
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 2e83256..d09c5f6 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -250,6 +250,9 @@
     if (classes & INPUT_DEVICE_CLASS_DPAD) {
         keyboardSources |= AINPUT_SOURCE_DPAD;
     }
+    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+        keyboardSources |= AINPUT_SOURCE_GAMEPAD;
+    }
 
     if (keyboardSources != 0) {
         device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType));
@@ -267,6 +270,11 @@
         device->addMapper(new SingleTouchInputMapper(device));
     }
 
+    // Joystick-like devices.
+    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+        device->addMapper(new JoystickInputMapper(device));
+    }
+
     return device;
 }
 
@@ -733,6 +741,16 @@
     return 0;
 }
 
+void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
+        const RawAbsoluteAxisInfo& axis, const char* name) {
+    if (axis.valid) {
+        dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
+                name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
+    } else {
+        dump.appendFormat(INDENT4 "%s: unknown range\n", name);
+    }
+}
+
 
 // --- SwitchInputMapper ---
 
@@ -875,7 +893,7 @@
 bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
     return scanCode < BTN_MOUSE
         || scanCode >= KEY_OK
-        || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI);
+        || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
 }
 
 void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
@@ -1511,25 +1529,16 @@
     mRawAxes.orientation.clear();
 }
 
-static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) {
-    if (axis.valid) {
-        dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
-                name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
-    } else {
-        dump.appendFormat(INDENT4 "%s: unknown range\n", name);
-    }
-}
-
 void TouchInputMapper::dumpRawAxes(String8& dump) {
     dump.append(INDENT3 "Raw Axes:\n");
-    dumpAxisInfo(dump, mRawAxes.x, "X");
-    dumpAxisInfo(dump, mRawAxes.y, "Y");
-    dumpAxisInfo(dump, mRawAxes.pressure, "Pressure");
-    dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
-    dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
-    dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
-    dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
-    dumpAxisInfo(dump, mRawAxes.orientation, "Orientation");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.pressure, "Pressure");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation");
 }
 
 bool TouchInputMapper::configureSurfaceLocked() {
@@ -3697,4 +3706,199 @@
 }
 
 
+// --- JoystickInputMapper ---
+
+JoystickInputMapper::JoystickInputMapper(InputDevice* device) :
+        InputMapper(device) {
+    initialize();
+}
+
+JoystickInputMapper::~JoystickInputMapper() {
+}
+
+uint32_t JoystickInputMapper::getSources() {
+    return AINPUT_SOURCE_JOYSTICK;
+}
+
+void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    if (mAxes.x.valid) {
+        info->addMotionRange(AINPUT_MOTION_RANGE_X,
+                mAxes.x.min, mAxes.x.max, mAxes.x.flat, mAxes.x.fuzz);
+    }
+    if (mAxes.y.valid) {
+        info->addMotionRange(AINPUT_MOTION_RANGE_Y,
+                mAxes.y.min, mAxes.y.max, mAxes.y.flat, mAxes.y.fuzz);
+    }
+}
+
+void JoystickInputMapper::dump(String8& dump) {
+    dump.append(INDENT2 "Joystick Input Mapper:\n");
+
+    dump.append(INDENT3 "Raw Axes:\n");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y");
+
+    dump.append(INDENT3 "Normalized Axes:\n");
+    dumpNormalizedAxis(dump, mAxes.x, "X");
+    dumpNormalizedAxis(dump, mAxes.y, "Y");
+    dumpNormalizedAxis(dump, mAxes.hat0X, "Hat0X");
+    dumpNormalizedAxis(dump, mAxes.hat0Y, "Hat0Y");
+}
+
+void JoystickInputMapper::dumpNormalizedAxis(String8& dump,
+        const NormalizedAxis& axis, const char* name) {
+    if (axis.valid) {
+        dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, "
+                "scale=%0.3f, center=%0.3f, precision=%0.3f, value=%0.3f\n",
+                name, axis.min, axis.max, axis.flat, axis.fuzz,
+                axis.scale, axis.center, axis.precision, axis.value);
+    } else {
+        dump.appendFormat(INDENT4 "%s: unknown range\n", name);
+    }
+}
+
+void JoystickInputMapper::configure() {
+    InputMapper::configure();
+
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0X, & mRawAxes.hat0X);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0Y, & mRawAxes.hat0Y);
+
+    mAxes.x.configure(mRawAxes.x);
+    mAxes.y.configure(mRawAxes.y);
+    mAxes.hat0X.configure(mRawAxes.hat0X);
+    mAxes.hat0Y.configure(mRawAxes.hat0Y);
+}
+
+void JoystickInputMapper::initialize() {
+    mAccumulator.clear();
+
+    mAxes.x.resetState();
+    mAxes.y.resetState();
+    mAxes.hat0X.resetState();
+    mAxes.hat0Y.resetState();
+}
+
+void JoystickInputMapper::reset() {
+    // Recenter all axes.
+    nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+    mAccumulator.clear();
+    mAccumulator.fields = Accumulator::FIELD_ALL;
+    sync(when);
+
+    // Reinitialize state.
+    initialize();
+
+    InputMapper::reset();
+}
+
+void JoystickInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+    case EV_ABS:
+        switch (rawEvent->scanCode) {
+        case ABS_X:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_X;
+            mAccumulator.absX = rawEvent->value;
+            break;
+        case ABS_Y:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_Y;
+            mAccumulator.absY = rawEvent->value;
+            break;
+        case ABS_HAT0X:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0X;
+            mAccumulator.absHat0X = rawEvent->value;
+            break;
+        case ABS_HAT0Y:
+            mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0Y;
+            mAccumulator.absHat0Y = rawEvent->value;
+            break;
+        }
+        break;
+
+    case EV_SYN:
+        switch (rawEvent->scanCode) {
+        case SYN_REPORT:
+            sync(rawEvent->when);
+            break;
+        }
+        break;
+    }
+}
+
+void JoystickInputMapper::sync(nsecs_t when) {
+    uint32_t fields = mAccumulator.fields;
+    if (fields == 0) {
+        return; // no new state changes, so nothing to do
+    }
+
+    int32_t metaState = mContext->getGlobalMetaState();
+
+    bool motionAxisChanged = false;
+    if (fields & Accumulator::FIELD_ABS_X) {
+        if (mAxes.x.updateValue(mAccumulator.absX)) {
+            motionAxisChanged = true;
+        }
+    }
+
+    if (fields & Accumulator::FIELD_ABS_Y) {
+        if (mAxes.y.updateValue(mAccumulator.absY)) {
+            motionAxisChanged = true;
+        }
+    }
+
+    if (motionAxisChanged) {
+        PointerCoords pointerCoords;
+        pointerCoords.x = mAxes.x.value;
+        pointerCoords.y = mAxes.y.value;
+        pointerCoords.touchMajor = 0;
+        pointerCoords.touchMinor = 0;
+        pointerCoords.toolMajor = 0;
+        pointerCoords.toolMinor = 0;
+        pointerCoords.pressure = 0;
+        pointerCoords.size = 0;
+        pointerCoords.orientation = 0;
+
+        int32_t pointerId = 0;
+        getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+                AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                1, &pointerId, &pointerCoords, mAxes.x.precision, mAxes.y.precision, 0);
+    }
+
+    if (fields & Accumulator::FIELD_ABS_HAT0X) {
+        if (mAxes.hat0X.updateValueAndDirection(mAccumulator.absHat0X)) {
+            notifyDirectionalAxis(mAxes.hat0X, when, metaState,
+                    AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT);
+        }
+    }
+
+    if (fields & Accumulator::FIELD_ABS_HAT0Y) {
+        if (mAxes.hat0Y.updateValueAndDirection(mAccumulator.absHat0Y)) {
+            notifyDirectionalAxis(mAxes.hat0Y, when, metaState,
+                    AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN);
+        }
+    }
+
+    mAccumulator.clear();
+}
+
+void JoystickInputMapper::notifyDirectionalAxis(DirectionalAxis& axis,
+        nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode) {
+    if (axis.lastKeyCode) {
+        getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+                AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM,
+                axis.lastKeyCode, 0, metaState, when);
+        axis.lastKeyCode = 0;
+    }
+    if (axis.direction) {
+        axis.lastKeyCode = axis.direction > 0 ? highKeyCode : lowKeyCode;
+        getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0,
+                AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM,
+                axis.lastKeyCode, 0, metaState, when);
+    }
+}
+
+
 } // namespace android
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 7619682..27cb8e1 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -354,6 +354,9 @@
 protected:
     InputDevice* mDevice;
     InputReaderContext* mContext;
+
+    static void dumpRawAbsoluteAxisInfo(String8& dump,
+            const RawAbsoluteAxisInfo& axis, const char* name);
 };
 
 
@@ -968,6 +971,139 @@
     void sync(nsecs_t when);
 };
 
+
+class JoystickInputMapper : public InputMapper {
+public:
+    JoystickInputMapper(InputDevice* device);
+    virtual ~JoystickInputMapper();
+
+    virtual uint32_t getSources();
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
+    virtual void configure();
+    virtual void reset();
+    virtual void process(const RawEvent* rawEvent);
+
+private:
+    struct RawAxes {
+        RawAbsoluteAxisInfo x;
+        RawAbsoluteAxisInfo y;
+        RawAbsoluteAxisInfo hat0X;
+        RawAbsoluteAxisInfo hat0Y;
+    } mRawAxes;
+
+    struct NormalizedAxis {
+        bool valid;
+
+        static const float min = -1.0f;
+        static const float max = -1.0f;
+
+        float scale;      // scale factor
+        float center;     // center offset after scaling
+        float precision;  // precision
+        float flat;       // size of flat region
+        float fuzz;       // error tolerance
+
+        float value;      // most recent value
+
+        NormalizedAxis() : valid(false), scale(0), center(0), precision(0),
+                flat(0), fuzz(0), value(0) {
+        }
+
+        void configure(const RawAbsoluteAxisInfo& rawAxis) {
+            if (rawAxis.valid && rawAxis.getRange() != 0) {
+                valid = true;
+                scale = 2.0f / rawAxis.getRange();
+                precision = rawAxis.getRange();
+                flat = rawAxis.flat * scale;
+                fuzz = rawAxis.fuzz * scale;
+                center = float(rawAxis.minValue + rawAxis.maxValue) / rawAxis.getRange();
+            }
+        }
+
+        void resetState() {
+            value = 0;
+        }
+
+        bool updateValue(int32_t rawValue) {
+            float newValue = rawValue * scale - center;
+            if (value == newValue) {
+                return false;
+            }
+            value = newValue;
+            return true;
+        }
+    };
+
+    struct DirectionalAxis : NormalizedAxis {
+        int32_t direction; // most recent direction vector: value is one of -1, 0, 1.
+
+        int32_t lastKeyCode;  // most recent key code produced
+
+        DirectionalAxis() : lastKeyCode(0) {
+        }
+
+        void resetState() {
+            NormalizedAxis::resetState();
+            direction = 0;
+            lastKeyCode = 0;
+        }
+
+        bool updateValueAndDirection(int32_t rawValue) {
+            if (!updateValue(rawValue)) {
+                return false;
+            }
+            if (value > flat) {
+                direction = 1;
+            } else if (value < -flat) {
+                direction = -1;
+            } else {
+                direction = 0;
+            }
+            return true;
+        }
+    };
+
+    struct Axes {
+        NormalizedAxis x;
+        NormalizedAxis y;
+        DirectionalAxis hat0X;
+        DirectionalAxis hat0Y;
+    } mAxes;
+
+    struct Accumulator {
+        enum {
+            FIELD_ABS_X = 1,
+            FIELD_ABS_Y = 2,
+            FIELD_ABS_HAT0X = 4,
+            FIELD_ABS_HAT0Y = 8,
+
+            FIELD_ALL = FIELD_ABS_X | FIELD_ABS_Y | FIELD_ABS_HAT0X | FIELD_ABS_HAT0Y,
+        };
+
+        uint32_t fields;
+
+        int32_t absX;
+        int32_t absY;
+        int32_t absHat0X;
+        int32_t absHat0Y;
+
+        inline void clear() {
+            fields = 0;
+        }
+    } mAccumulator;
+
+    void initialize();
+
+    void sync(nsecs_t when);
+
+    void notifyDirectionalAxis(DirectionalAxis& axis,
+            nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode);
+
+    static void dumpNormalizedAxis(String8& dump,
+            const NormalizedAxis& axis, const char* name);
+};
+
 } // namespace android
 
 #endif // _UI_INPUT_READER_H
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index bd3c554..f98b7e9 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2132,7 +2132,8 @@
         if (proxy == null) proxy = new ProxyProperties("", 0, "");
         log("sending Proxy Broadcast for " + proxy);
         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
+            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
         mContext.sendStickyBroadcast(intent);
     }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 978946f..62ff064 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -405,6 +405,7 @@
                 Slog.i(TAG, "USB Observer");
                 // Listen for USB changes
                 usb = new UsbService(context);
+                ServiceManager.addService(Context.USB_SERVICE, usb);
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting UsbService", e);
             }
diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/UsbService.java
index 8ef03d4..5c03fb2 100644
--- a/services/java/com/android/server/UsbService.java
+++ b/services/java/com/android/server/UsbService.java
@@ -19,10 +19,18 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.IUsbManager;
+import android.hardware.UsbConstants;
+import android.hardware.UsbDevice;
+import android.hardware.UsbEndpoint;
+import android.hardware.UsbInterface;
 import android.hardware.UsbManager;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
 import android.os.UEventObserver;
 import android.provider.Settings;
 import android.util.Log;
@@ -32,11 +40,12 @@
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * <p>UsbService monitors for changes to USB state.
  */
-class UsbService {
+class UsbService extends IUsbManager.Stub {
     private static final String TAG = UsbService.class.getSimpleName();
     private static final boolean LOG = false;
 
@@ -68,10 +77,12 @@
     private int mLastConnected = -1;
     private int mLastConfiguration = -1;
 
-    // lists of enabled and disabled USB functions
+    // lists of enabled and disabled USB functions (for USB device mode)
     private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
     private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
 
+    private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
+
     private boolean mSystemReady;
 
     private final Context mContext;
@@ -186,8 +197,106 @@
         }
     }
 
+    // called from JNI in monitorUsbHostBus()
+    private void usbDeviceAdded(String deviceName, int vendorID, int productID,
+            int deviceClass, int deviceSubclass, int deviceProtocol,
+            /* array of quintuples containing id, class, subclass, protocol
+               and number of endpoints for each interface */
+            int[] interfaceValues,
+           /* array of quadruples containing address, attributes, max packet size
+              and interval for each endpoint */
+            int[] endpointValues) {
+
+        // ignore hubs
+        if (deviceClass == UsbConstants.USB_CLASS_HUB) {
+            return;
+        }
+
+        synchronized (mDevices) {
+            if (mDevices.get(deviceName) != null) {
+                Log.w(TAG, "device already on mDevices list: " + deviceName);
+                return;
+            }
+
+            int numInterfaces = interfaceValues.length / 5;
+            Parcelable[] interfaces = new UsbInterface[numInterfaces];
+            try {
+                // repackage interfaceValues as an array of UsbInterface
+                int intf, endp, ival = 0, eval = 0;
+                boolean hasGoodInterface = false;
+                for (intf = 0; intf < numInterfaces; intf++) {
+                    int interfaceId = interfaceValues[ival++];
+                    int interfaceClass = interfaceValues[ival++];
+                    int interfaceSubclass = interfaceValues[ival++];
+                    int interfaceProtocol = interfaceValues[ival++];
+                    int numEndpoints = interfaceValues[ival++];
+
+                    Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
+                    for (endp = 0; endp < numEndpoints; endp++) {
+                        int address = endpointValues[eval++];
+                        int attributes = endpointValues[eval++];
+                        int maxPacketSize = endpointValues[eval++];
+                        int interval = endpointValues[eval++];
+                        endpoints[endp] = new UsbEndpoint(address, attributes,
+                                maxPacketSize, interval);
+                    }
+
+                    if (interfaceClass != UsbConstants.USB_CLASS_HUB) {
+                        hasGoodInterface = true;
+                    }
+                    interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
+                            interfaceSubclass, interfaceProtocol, endpoints);
+                }
+
+                if (!hasGoodInterface) {
+                    return;
+                }
+            } catch (Exception e) {
+                // beware of index out of bound exceptions, which might happen if
+                // a device does not set bNumEndpoints correctly
+                Log.e(TAG, "error parsing USB descriptors", e);
+                return;
+            }
+
+            UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
+                    deviceClass, deviceSubclass, deviceProtocol, interfaces);
+            mDevices.put(deviceName, device);
+
+            Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+            intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName);
+            intent.putExtra(UsbManager.EXTRA_VENDOR_ID, vendorID);
+            intent.putExtra(UsbManager.EXTRA_PRODUCT_ID, productID);
+            intent.putExtra(UsbManager.EXTRA_DEVICE_CLASS, deviceClass);
+            intent.putExtra(UsbManager.EXTRA_DEVICE_SUBCLASS, deviceSubclass);
+            intent.putExtra(UsbManager.EXTRA_DEVICE_PROTOCOL, deviceProtocol);
+            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+            Log.d(TAG, "usbDeviceAdded, sending " + intent);
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    // called from JNI in monitorUsbHostBus()
+    private void usbDeviceRemoved(String deviceName) {
+        synchronized (mDevices) {
+            UsbDevice device = mDevices.remove(deviceName);
+            if (device != null) {
+                Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
+                intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName);
+                Log.d(TAG, "usbDeviceRemoved, sending " + intent);
+                mContext.sendBroadcast(intent);
+            }
+        }
+    }
+
     private void initHostSupport() {
-        // temporarily disabled
+        // Create a thread to call into native code to wait for USB host events.
+        // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
+        Runnable runnable = new Runnable() {
+            public void run() {
+                monitorUsbHostBus();
+            }
+        };
+        new Thread(null, runnable, "UsbService host thread").start();
     }
 
     void systemReady() {
@@ -208,6 +317,21 @@
         mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0);
     }
 
+    /* Returns a list of all currently attached USB devices */
+    public void getDeviceList(Bundle devices) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
+        synchronized (mDevices) {
+            for (String name : mDevices.keySet()) {
+                devices.putParcelable(name, mDevices.get(name));
+            }
+        }
+    }
+
+    public ParcelFileDescriptor openDevice(String deviceName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
+        return nativeOpenDevice(deviceName);
+    }
+
     private final Handler mHandler = new Handler() {
         private void addEnabledFunctions(Intent intent) {
             // include state of all USB functions in our extras
@@ -249,4 +373,7 @@
             }
         }
     };
+
+    private native void monitorUsbHostBus();
+    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
 }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index cf07239..04f4e4e7 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -39,7 +39,6 @@
 import android.net.wifi.WpsConfiguration;
 import android.net.wifi.WpsResult;
 import android.net.ConnectivityManager;
-import android.net.InterfaceConfiguration;
 import android.net.DhcpInfo;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
@@ -56,7 +55,6 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
-import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -113,9 +111,6 @@
 
     private final IBatteryStats mBatteryStats;
 
-    ConnectivityManager mCm;
-    private String[] mWifiRegexs;
-
     /**
      * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
      * Settings.Secure value is not present. This timeout value is chosen as
@@ -256,20 +251,6 @@
                 },
                 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
 
-        mContext.registerReceiver(
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-
-                    ArrayList<String> available = intent.getStringArrayListExtra(
-                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
-                    ArrayList<String> active = intent.getStringArrayListExtra(
-                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
-                    updateTetherState(available, active);
-
-                }
-            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
-
         IntentFilter filter = new IntentFilter();
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
@@ -322,52 +303,6 @@
         setWifiEnabled(wifiEnabled);
     }
 
-    private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
-
-        boolean wifiTethered = false;
-        boolean wifiAvailable = false;
-
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
-        if (mCm == null) {
-            mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        }
-
-        mWifiRegexs = mCm.getTetherableWifiRegexs();
-
-        for (String intf : available) {
-            for (String regex : mWifiRegexs) {
-                if (intf.matches(regex)) {
-
-                    InterfaceConfiguration ifcg = null;
-                    try {
-                        ifcg = service.getInterfaceConfig(intf);
-                        if (ifcg != null) {
-                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
-                            ifcg.addr = InetAddress.getByName("192.168.43.1");
-                            ifcg.mask = InetAddress.getByName("255.255.255.0");
-                            ifcg.interfaceFlags = "[up]";
-
-                            service.setInterfaceConfig(intf, ifcg);
-                        }
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
-                        setWifiApEnabled(null, false);
-                        return;
-                    }
-
-                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
-                        Slog.e(TAG, "Error tethering on " + intf);
-                        setWifiApEnabled(null, false);
-                        return;
-                    }
-                    break;
-                }
-            }
-        }
-    }
-
     private boolean testAndClearWifiSavedState() {
         final ContentResolver cr = mContext.getContentResolver();
         int wifiSavedState = 0;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
old mode 100755
new mode 100644
index 6bb19b0..8ea166f
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -9634,7 +9634,7 @@
                 // r.record is null if findServiceLocked() failed the caller permission check
                 if (r.record == null) {
                     throw new SecurityException(
-                            "Permission Denial: Accessing service "
+                            "Permission Denial: Accessing service " + r.record.name
                             + " from pid=" + Binder.getCallingPid()
                             + ", uid=" + Binder.getCallingUid()
                             + " requires " + r.permission);
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index dd9db9a..c847580 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -1187,8 +1187,13 @@
                 try {
                     service.startTethering(mDhcpRange);
                 } catch (Exception e) {
-                    transitionTo(mStartTetheringErrorState);
-                    return false;
+                    try {
+                        service.stopTethering();
+                        service.startTethering(mDhcpRange);
+                    } catch (Exception ee) {
+                        transitionTo(mStartTetheringErrorState);
+                        return false;
+                    }
                 }
                 try {
                     service.setDnsForwarders(mDnsServers);
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index f5a5b4d..be37d5d 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -12,6 +12,7 @@
     com_android_server_LightsService.cpp \
     com_android_server_PowerManagerService.cpp \
     com_android_server_SystemServer.cpp \
+    com_android_server_UsbService.cpp \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
     onload.cpp
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp
new file mode 100644
index 0000000..ef22111
--- /dev/null
+++ b/services/jni/com_android_server_UsbService.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbService"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "utils/Vector.h"
+
+#include <usbhost/usbhost.h>
+
+#include <stdio.h>
+#include <asm/byteorder.h>
+
+namespace android
+{
+
+static struct file_descriptor_offsets_t
+{
+    jclass mClass;
+    jmethodID mConstructor;
+    jfieldID mDescriptor;
+} gFileDescriptorOffsets;
+
+static struct parcel_file_descriptor_offsets_t
+{
+    jclass mClass;
+    jmethodID mConstructor;
+} gParcelFileDescriptorOffsets;
+
+static jmethodID method_usbDeviceAdded;
+static jmethodID method_usbDeviceRemoved;
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+static int usb_device_added(const char *devname, void* client_data) {
+    struct usb_descriptor_header* desc;
+    struct usb_descriptor_iter iter;
+
+    struct usb_device *device = usb_device_open(devname);
+    if (!device) {
+        LOGE("usb_device_open failed\n");
+        return 0;
+    }
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jobject thiz = (jobject)client_data;
+    Vector<int> interfaceValues;
+    Vector<int> endpointValues;
+    const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
+
+    uint16_t vendorId = usb_device_get_vendor_id(device);
+    uint16_t productId = usb_device_get_product_id(device);
+    uint8_t deviceClass = deviceDesc->bDeviceClass;
+    uint8_t deviceSubClass = deviceDesc->bDeviceSubClass;
+    uint8_t protocol = deviceDesc->bDeviceProtocol;
+
+    usb_descriptor_iter_init(device, &iter);
+
+    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+        if (desc->bDescriptorType == USB_DT_INTERFACE) {
+            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+            // push class, subclass, protocol and number of endpoints into interfaceValues vector
+            interfaceValues.add(interface->bInterfaceNumber);
+            interfaceValues.add(interface->bInterfaceClass);
+            interfaceValues.add(interface->bInterfaceSubClass);
+            interfaceValues.add(interface->bInterfaceProtocol);
+            interfaceValues.add(interface->bNumEndpoints);
+        } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+            struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
+
+            // push address, attributes, max packet size and interval into endpointValues vector
+            endpointValues.add(endpoint->bEndpointAddress);
+            endpointValues.add(endpoint->bmAttributes);
+            endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize));
+            endpointValues.add(endpoint->bInterval);
+        }
+    }
+
+    usb_device_close(device);
+
+    // handle generic device notification
+    int length = interfaceValues.size();
+    jintArray interfaceArray = env->NewIntArray(length);
+    env->SetIntArrayRegion(interfaceArray, 0, length, interfaceValues.array());
+
+    length = endpointValues.size();
+    jintArray endpointArray = env->NewIntArray(length);
+    env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array());
+
+    env->CallVoidMethod(thiz, method_usbDeviceAdded,
+            env->NewStringUTF(devname), vendorId, productId, deviceClass,
+            deviceSubClass, protocol, interfaceArray, endpointArray);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+    return 0;
+}
+
+static int usb_device_removed(const char *devname, void* client_data) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jobject thiz = (jobject)client_data;
+
+    env->CallVoidMethod(thiz, method_usbDeviceRemoved, env->NewStringUTF(devname));
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return 0;
+}
+
+static void android_server_UsbService_monitorUsbHostBus(JNIEnv *env, jobject thiz)
+{
+    struct usb_host_context* context = usb_host_init();
+    if (!context) {
+        LOGE("usb_host_init failed");
+        return;
+    }
+    // this will never return so it is safe to pass thiz directly
+    usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
+}
+
+static jobject android_server_UsbService_openDevice(JNIEnv *env, jobject thiz, jstring deviceName)
+{
+    const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+    struct usb_device* device = usb_device_open(deviceNameStr);
+    env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+
+    if (!device)
+        return NULL;
+
+    int fd = usb_device_get_fd(device);
+    if (fd < 0)
+        return NULL;
+    int newFD = dup(fd);
+    usb_device_close(device);
+
+    jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
+        gFileDescriptorOffsets.mConstructor);
+    if (fileDescriptor != NULL) {
+        env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, newFD);
+    } else {
+        return NULL;
+    }
+    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
+        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
+}
+
+static JNINativeMethod method_table[] = {
+    { "monitorUsbHostBus", "()V", (void*)android_server_UsbService_monitorUsbHostBus },
+    { "nativeOpenDevice",  "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
+                                  (void*)android_server_UsbService_openDevice },
+};
+
+int register_android_server_UsbService(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("com/android/server/UsbService");
+    if (clazz == NULL) {
+        LOGE("Can't find com/android/server/UsbService");
+        return -1;
+    }
+    method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V");
+    if (method_usbDeviceAdded == NULL) {
+        LOGE("Can't find usbDeviceAdded");
+        return -1;
+    }
+    method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V");
+    if (method_usbDeviceRemoved == NULL) {
+        LOGE("Can't find usbDeviceRemoved");
+        return -1;
+    }
+
+   clazz = env->FindClass("java/io/FileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+    gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
+    gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
+    LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
+                 "Unable to find descriptor field in java.io.FileDescriptor");
+
+   clazz = env->FindClass("android/os/ParcelFileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
+    gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
+    LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
+                 "Unable to find constructor for android.os.ParcelFileDescriptor");
+
+    return jniRegisterNativeMethods(env, "com/android/server/UsbService",
+            method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index bdd6d80..37b520b 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -13,6 +13,7 @@
 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_UsbService(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
@@ -40,6 +41,7 @@
     register_android_server_LightsService(env);
     register_android_server_AlarmManagerService(env);
     register_android_server_BatteryService(env);
+    register_android_server_UsbService(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GpsLocationProvider(env);
diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java
index d0f3d24..df12153 100644
--- a/telephony/java/com/android/internal/telephony/DataCallState.java
+++ b/telephony/java/com/android/internal/telephony/DataCallState.java
@@ -17,16 +17,43 @@
 
 package com.android.internal.telephony;
 
+/**
+ * This is RIL_Data_Call_Response_v5 from ril.h
+ * TODO: Rename to DataCallResponse.
+ */
 public class DataCallState {
-    public int cid;
-    public int active;
-    public String type;
-    public String apn;
-    public String address;
+    public int version = 0;
+    public int status = 0;
+    public int cid = 0;
+    public int active = 0;
+    public String type = "";
+    public String ifname = "";
+    public String [] addresses = new String[0];
+    public String [] dnses = new String[0];
 
     @Override
     public String toString() {
-        return "DataCallState: {" + " cid: " + cid + ", active: " + active + ", type: " + type
-                + ", apn: " + apn + ", address: " + address + " }";
+        StringBuffer sb = new StringBuffer();
+        sb.append("DataCallState: {")
+           .append("version=").append(version)
+           .append(" status=").append(status)
+           .append(" cid=").append(cid)
+           .append(" active=").append(active)
+           .append(" type=").append(type)
+           .append("' ifname='").append(ifname);
+        sb.append("' addresses=[");
+        for (String addr : addresses) {
+            sb.append(addr);
+            sb.append(",");
+        }
+        if (addresses.length > 0) sb.deleteCharAt(sb.length()-1);
+        sb.append("] dnses=[");
+        for (String addr : dnses) {
+            sb.append(addr);
+            sb.append(",");
+        }
+        if (dnses.length > 0) sb.deleteCharAt(sb.length()-1);
+        sb.append("]}");
+        return sb.toString();
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index a3d3781..31c89d0 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -27,14 +27,11 @@
 import android.os.Message;
 import android.os.SystemProperties;
 import android.text.TextUtils;
-import android.util.EventLog;
 
 import java.net.InetAddress;
 import java.net.Inet4Address;
-import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
 import java.net.UnknownHostException;
+import java.util.HashMap;
 
 /**
  * {@hide}
@@ -56,48 +53,6 @@
  * <code>AsyncResult.exception = new Exception()</code>.
  *
  * The other public methods are provided for debugging.
- *
- * Below is the state machine description for this class.
- *
- * DataConnection {
- *   + mDefaultState {
- *        EVENT_RESET { clearSettings, notifiyDisconnectCompleted, >mInactiveState }.
- *        EVENT_CONNECT {  notifyConnectCompleted(FailCause.UNKNOWN) }.
- *        EVENT_DISCONNECT { notifyDisconnectCompleted }.
- *
- *        // Ignored messages
- *        EVENT_SETUP_DATA_CONNECTION_DONE,
- *        EVENT_GET_LAST_FAIL_DONE,
- *        EVENT_DEACTIVATE_DONE.
- *     }
- *   ++ # mInactiveState
- *        e(doNotifications)
- *        x(clearNotifications) {
- *            EVENT_RESET { notifiyDisconnectCompleted }.
- *            EVENT_CONNECT {startConnecting, >mActivatingState }.
- *        }
- *   ++   mActivatingState {
- *            EVENT_DISCONNECT { %EVENT_DISCONNECT }.
- *            EVENT_SETUP_DATA_CONNECTION_DONE {
- *                  if (SUCCESS) { notifyConnectCompleted(FailCause.NONE), >mActiveState }.
- *                  if (ERR_BadCommand) {
- *                         notifyConnectCompleted(FailCause.UNKNOWN), >mInactiveState }.
- *                  if (ERR_BadDns) { tearDownData($DEACTIVATE_DONE), >mDisconnectingBadDnsState }.
- *                  if (ERR_Other) { getLastDataCallFailCause($EVENT_GET_LAST_FAIL_DONE) }.
- *                  if (ERR_Stale) {}.
- *            }
- *            EVENT_GET_LAST_FAIL_DONE { notifyConnectCompleted(result), >mInactive }.
- *        }
- *   ++   mActiveState {
- *            EVENT_DISCONNECT { tearDownData($EVENT_DEACTIVATE_DONE), >mDisconnecting }.
- *        }
- *   ++   mDisconnectingState {
- *            EVENT_DEACTIVATE_DONE { notifyDisconnectCompleted, >mInactiveState }.
- *        }
- *   ++   mDisconnectingBadDnsState {
- *            EVENT_DEACTIVATE_DONE { notifyConnectComplete(FailCause.UNKNOWN), >mInactiveState }.
- *        }
- *  }
  */
 public abstract class DataConnection extends HierarchicalStateMachine {
     protected static final boolean DBG = true;
@@ -109,24 +64,22 @@
      * Class returned by onSetupConnectionCompleted.
      */
     protected enum SetupResult {
+        SUCCESS,
         ERR_BadCommand,
-        ERR_BadDns,
-        ERR_Other,
+        ERR_UnacceptableParameter,
+        ERR_GetLastErrorFromRil,
         ERR_Stale,
-        SUCCESS;
+        ERR_RilError;
 
         public FailCause mFailCause;
 
+        SetupResult() {
+            mFailCause = FailCause.fromInt(0);
+        }
+
         @Override
         public String toString() {
-            switch (this) {
-                case ERR_BadCommand: return "Bad Command";
-                case ERR_BadDns: return "Bad DNS";
-                case ERR_Other: return "Other error";
-                case ERR_Stale: return "Stale command";
-                case SUCCESS: return "SUCCESS";
-                default: return "unknown";
-            }
+            return name() + "  SetupResult.mFailCause=" + mFailCause;
         }
     }
 
@@ -168,31 +121,66 @@
     }
 
     /**
-     * Returned as the reason for a connection failure.
+     * Returned as the reason for a connection failure as defined
+     * by RIL_DataCallFailCause in ril.h and some local errors.
      */
     public enum FailCause {
-        NONE,
-        OPERATOR_BARRED,
-        INSUFFICIENT_RESOURCES,
-        MISSING_UNKNOWN_APN,
-        UNKNOWN_PDP_ADDRESS,
-        USER_AUTHENTICATION,
-        ACTIVATION_REJECT_GGSN,
-        ACTIVATION_REJECT_UNSPECIFIED,
-        SERVICE_OPTION_NOT_SUPPORTED,
-        SERVICE_OPTION_NOT_SUBSCRIBED,
-        SERVICE_OPTION_OUT_OF_ORDER,
-        NSAPI_IN_USE,
-        PROTOCOL_ERRORS,
-        REGISTRATION_FAIL,
-        GPRS_REGISTRATION_FAIL,
-        UNKNOWN,
+        NONE(0),
 
-        RADIO_NOT_AVAILABLE;
+        // This series of errors as specified by the standards
+        // specified in ril.h
+        OPERATOR_BARRED(0x08),
+        INSUFFICIENT_RESOURCES(0x1A),
+        MISSING_UNKNOWN_APN(0x1B),
+        UNKNOWN_PDP_ADDRESS_TYPE(0x1C),
+        USER_AUTHENTICATION(0x1D),
+        ACTIVATION_REJECT_GGSN(0x1E),
+        ACTIVATION_REJECT_UNSPECIFIED(0x1F),
+        SERVICE_OPTION_NOT_SUPPORTED(0x20),
+        SERVICE_OPTION_NOT_SUBSCRIBED(0x21),
+        SERVICE_OPTION_OUT_OF_ORDER(0x22),
+        NSAPI_IN_USE(0x23),
+        ONLY_IPV4_ALLOWED(0x32),
+        ONLY_IPV6_ALLOWED(0x33),
+        ONLY_SINGLE_BEARER_ALLOWED(0x34),
+        PROTOCOL_ERRORS(0x6F),
+
+        // Local errors generated by Vendor RIL
+        // specified in ril.h
+        REGISTRATION_FAIL(-1),
+        GPRS_REGISTRATION_FAIL(-2),
+        SIGNAL_LOST(-3),
+        PREF_RADIO_TECH_CHANGED(-4),
+        RADIO_POWER_OFF(-5),
+        TETHERED_CALL_ACTIVE(-6),
+        ERROR_UNSPECIFIED(0xFFFF),
+
+        // Errors generated by the Framework
+        // specified here
+        UNKNOWN(0x10000),
+        RADIO_NOT_AVAILABLE(0x10001),
+        UNACCEPTABLE_NETWORK_PARAMETER(0x10002);
+
+        private final int mErrorCode;
+        private static final HashMap<Integer, FailCause> sErrorCodeToFailCauseMap;
+        static {
+            sErrorCodeToFailCauseMap = new HashMap<Integer, FailCause>();
+            for (FailCause fc : values()) {
+                sErrorCodeToFailCauseMap.put(fc.ordinal(), fc);
+            }
+        }
+
+        FailCause(int errorCode) {
+            mErrorCode = errorCode;
+        }
+
+        int getErrorCode() {
+            return mErrorCode;
+        }
 
         public boolean isPermanentFail() {
             return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
-                   (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) ||
+                   (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
                    (this == SERVICE_OPTION_NOT_SUPPORTED) ||
                    (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) ||
                    (this == PROTOCOL_ERRORS);
@@ -200,52 +188,21 @@
 
         public boolean isEventLoggable() {
             return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
-                    (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) ||
+                    (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
                     (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
                     (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
                     (this == SERVICE_OPTION_NOT_SUPPORTED) ||
                     (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
-                    (this == PROTOCOL_ERRORS);
+                    (this == PROTOCOL_ERRORS) ||
+                    (this == UNACCEPTABLE_NETWORK_PARAMETER);
         }
 
-        @Override
-        public String toString() {
-            switch (this) {
-            case NONE:
-                return "No Error";
-            case OPERATOR_BARRED:
-                return "Operator Barred";
-            case INSUFFICIENT_RESOURCES:
-                return "Insufficient Resources";
-            case MISSING_UNKNOWN_APN:
-                return "Missing / Unknown APN";
-            case UNKNOWN_PDP_ADDRESS:
-                return "Unknown PDP Address";
-            case USER_AUTHENTICATION:
-                return "Error User Authentication";
-            case ACTIVATION_REJECT_GGSN:
-                return "Activation Reject GGSN";
-            case ACTIVATION_REJECT_UNSPECIFIED:
-                return "Activation Reject unspecified";
-            case SERVICE_OPTION_NOT_SUPPORTED:
-                return "Data Not Supported";
-            case SERVICE_OPTION_NOT_SUBSCRIBED:
-                return "Data Not subscribed";
-            case SERVICE_OPTION_OUT_OF_ORDER:
-                return "Data Services Out of Order";
-            case NSAPI_IN_USE:
-                return "NSAPI in use";
-            case PROTOCOL_ERRORS:
-                return "Protocol Errors";
-            case REGISTRATION_FAIL:
-                return "Network Registration Failure";
-            case GPRS_REGISTRATION_FAIL:
-                return "Data Network Registration Failure";
-            case RADIO_NOT_AVAILABLE:
-                return "Radio Not Available";
-            default:
-                return "Unknown Data Error";
+        public static FailCause fromInt(int errorCode) {
+            FailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
+            if (fc == null) {
+                fc = UNKNOWN;
             }
+            return fc;
         }
     }
 
@@ -275,12 +232,11 @@
     Object userData;
 
     //***** Abstract methods
+    @Override
     public abstract String toString();
 
     protected abstract void onConnect(ConnectionParams cp);
 
-    protected abstract FailCause getFailCauseFromRequest(int rilCause);
-
     protected abstract boolean isDnsOk(String[] domainNameServers);
 
     protected abstract void log(String s);
@@ -301,7 +257,7 @@
             addState(mActivatingState, mDefaultState);
             addState(mActiveState, mDefaultState);
             addState(mDisconnectingState, mDefaultState);
-            addState(mDisconnectingBadDnsState, mDefaultState);
+            addState(mDisconnectingErrorCreatingConnection, mDefaultState);
         setInitialState(mInactiveState);
         if (DBG) log("DataConnection constructor X");
     }
@@ -409,20 +365,26 @@
      * @return SetupResult.
      */
     private SetupResult onSetupConnectionCompleted(AsyncResult ar) {
-        SetupResult result;
-        String[] response = ((String[]) ar.result);
+        DataCallState response = (DataCallState) ar.result;
         ConnectionParams cp = (ConnectionParams) ar.userObj;
+        SetupResult result;
 
         if (ar.exception != null) {
-            if (DBG) log("DataConnection Init failed " + ar.exception);
+            if (DBG) {
+                log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
+                    " response=" + response);
+            }
 
             if (ar.exception instanceof CommandException
                     && ((CommandException) (ar.exception)).getCommandError()
                     == CommandException.Error.RADIO_NOT_AVAILABLE) {
                 result = SetupResult.ERR_BadCommand;
                 result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
+            } else if ((response == null) || (response.version < 4)) {
+                result = SetupResult.ERR_GetLastErrorFromRil;
             } else {
-                result = SetupResult.ERR_Other;
+                result = SetupResult.ERR_RilError;
+                result.mFailCause = FailCause.fromInt(response.status);
             }
         } else if (cp.tag != mTag) {
             if (DBG) {
@@ -430,112 +392,93 @@
             }
             result = SetupResult.ERR_Stale;
         } else {
-//            log("onSetupConnectionCompleted received " + response.length + " response strings:");
-//            for (int i = 0; i < response.length; i++) {
-//                log("  response[" + i + "]='" + response[i] + "'");
-//            }
+            log("onSetupConnectionCompleted received DataCallState: " + response);
 
             // Start with clean network properties and if we have
             // a failure we'll clear again at the bottom of this code.
             LinkProperties linkProperties = new LinkProperties();
-            if (response.length >= 2) {
-                cid = Integer.parseInt(response[0]);
-                String interfaceName = response[1];
-                result = SetupResult.SUCCESS;
-
+            if (response.status == FailCause.NONE.getErrorCode()) {
                 try {
-                    String prefix = "net." + interfaceName + ".";
+                    cid = response.cid;
+                    linkProperties.setInterfaceName(response.ifname);
+                    for (String addr : response.addresses) {
+                        LinkAddress la;
+                        if (!InetAddress.isNumeric(addr)) {
+                            EventLogTags.writeBadIpAddress(addr);
+                            throw new UnknownHostException("Non-numeric ip addr=" + addr);
+                        }
+                        InetAddress ia = InetAddress.getByName(addr);
+                        if (ia instanceof Inet4Address) {
+                            la = new LinkAddress(ia, 32);
+                        } else {
+                            la = new LinkAddress(ia, 128);
+                        }
+                        linkProperties.addLinkAddress(la);
+                    }
 
-                    NetworkInterface networkInterface = NetworkInterface.getByName(interfaceName);
-                    linkProperties.setInterfaceName(interfaceName);
-
-                    if (response.length >= 5) {
-                        log("response.length >=5 using response for ip='" + response[2] +
-                                "' dns='" + response[3] + "' gateway='" + response[4] + "'");
-                        String [] addresses = response[2].split(" ");
-                        String [] dnses = response[3].split(" ");
-                        String gateway = response[4];
-                        for (String addr : addresses) {
-                            LinkAddress la;
+                    if (response.dnses.length != 0) {
+                        for (String addr : response.dnses) {
                             if (!InetAddress.isNumeric(addr)) {
-                                throw new RuntimeException(
-                                        "Vendor ril bug: Non-numeric ip addr=" + addr);
+                                EventLogTags.writePdpBadDnsAddress("dns=" + addr); 
+                                throw new UnknownHostException("Non-numeric dns addr=" + addr);
                             }
                             InetAddress ia = InetAddress.getByName(addr);
-                            if (ia instanceof Inet4Address) {
-                                la = new LinkAddress(ia, 32);
-                            } else {
-                                la = new LinkAddress(ia, 128);
-                            }
-                            linkProperties.addLinkAddress(la);
+                            linkProperties.addDns(ia);
                         }
-
-                        if (dnses.length != 0) {
-                            for (String addr : dnses) {
-                                if (!InetAddress.isNumeric(addr)) {
-                                    throw new RuntimeException(
-                                            "Vendor ril bug: Non-numeric dns addr=" + addr);
-                                }
-                                InetAddress ia = InetAddress.getByName(addr);
-                                linkProperties.addDns(ia);
-                            }
-                            result = SetupResult.SUCCESS;
-                        } else {
-                            result = SetupResult.ERR_BadDns;
-                        }
-
-                        if (!InetAddress.isNumeric(gateway)) {
-                            throw new RuntimeException(
-                                    "Vendor ril bug: Non-numeric gateway addr=" + gateway);
-                        }
-                        linkProperties.setGateway(InetAddress.getByName(gateway));
-
+                        result = SetupResult.SUCCESS;
                     } else {
-                        log("response.length < 5 using properties for dns and gateway");
-                        for (InterfaceAddress addr : networkInterface.getInterfaceAddresses()) {
-                            linkProperties.addLinkAddress(new LinkAddress(addr));
-                        }
-
-                        String gatewayAddress = SystemProperties.get(prefix + "gw");
-                        linkProperties.setGateway(InetAddress.getByName(gatewayAddress));
+                        String prefix = "net." + response.ifname + ".";
 
                         String dnsServers[] = new String[2];
                         dnsServers[0] = SystemProperties.get(prefix + "dns1");
                         dnsServers[1] = SystemProperties.get(prefix + "dns2");
                         if (isDnsOk(dnsServers)) {
-                            linkProperties.addDns(InetAddress.getByName(dnsServers[0]));
-                            linkProperties.addDns(InetAddress.getByName(dnsServers[1]));
+                            for (String dnsAddr : dnsServers) {
+                                if (!InetAddress.isNumeric(dnsAddr)) {
+                                    EventLogTags.writePdpBadDnsAddress("dnsAddr=" + dnsAddr);
+                                    throw new UnknownHostException("Non-numeric dns addr="
+                                                + dnsAddr);
+                                }
+                                InetAddress ia = InetAddress.getByName(dnsAddr);
+                                linkProperties.addDns(ia);
+                            }
+                            result = SetupResult.SUCCESS;
                         } else {
-                            result = SetupResult.ERR_BadDns;
+                            StringBuilder sb = new StringBuilder();
+                            for (String dnsAddr : dnsServers) {
+                                sb.append(dnsAddr);
+                                sb.append(" ");
+                            }
+                            EventLogTags.writePdpBadDnsAddress("Unacceptable dns addresses=" + sb);
+                            throw new UnknownHostException("Unacceptable dns addresses=" + sb);
                         }
                     }
-                } catch (UnknownHostException e1) {
-                    log("onSetupCompleted: UnknowHostException " + e1);
-                    e1.printStackTrace();
-                    result = SetupResult.ERR_Other;
-                } catch (SocketException e2) {
-                    log("onSetupCompleted: SocketException " + e2);
-                    e2.printStackTrace();
-                    result = SetupResult.ERR_Other;
+                } catch (UnknownHostException e) {
+                    log("onSetupCompleted: UnknownHostException " + e);
+                    e.printStackTrace();
+                    result = SetupResult.ERR_UnacceptableParameter;
                 }
             } else {
-                log("onSetupCompleted: error; expected number of responses >= 2 was " +
-                        response.length);
-                result = SetupResult.ERR_Other;
+                if (response.version < 4) {
+                    result = SetupResult.ERR_GetLastErrorFromRil;
+                } else {
+                    result = SetupResult.ERR_RilError;
+                }
             }
 
             // An error occurred so clear properties
             if (result != SetupResult.SUCCESS) {
-                log("onSetupCompleted with an error clearing LinkProperties");
+                log("onSetupConnectionCompleted with an error, clearing LinkProperties");
                 linkProperties.clear();
             }
             mLinkProperties = linkProperties;
         }
 
         if (DBG) {
-            log("DataConnection setup result='" + result + "' on cid=" + cid);
+            log("onSetupConnectionCompleted: DataConnection setup result='"
+                    + result + "' on cid=" + cid);
             if (result == SetupResult.SUCCESS) {
-                log("LinkProperties: " + mLinkProperties.toString());
+                log("onSetupConnectionCompleted: LinkProperties: " + mLinkProperties.toString());
             }
         }
         return result;
@@ -684,6 +627,7 @@
                     cp = (ConnectionParams) ar.userObj;
 
                     SetupResult result = onSetupConnectionCompleted(ar);
+                    if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result);
                     switch (result) {
                         case SUCCESS:
                             // All is well
@@ -697,26 +641,21 @@
                             mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                             transitionTo(mInactiveState);
                             break;
-                        case ERR_BadDns:
-                            // Connection succeeded but DNS info is bad so disconnect
-                            StringBuilder dnsAddressesSb = new StringBuilder();
-                            for (InetAddress addr : mLinkProperties.getDnses()) {
-                                if (dnsAddressesSb.length() != 0) dnsAddressesSb.append(" ");
-                                dnsAddressesSb.append(addr.toString());
-                            }
-                            if (dnsAddressesSb.length() == 0) {
-                                dnsAddressesSb.append("no-dns-addresses");
-                            }
-                            EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS,
-                                    dnsAddressesSb.toString());
+                        case ERR_UnacceptableParameter:
+                            // The addresses given from the RIL are bad
                             tearDownData(cp);
-                            transitionTo(mDisconnectingBadDnsState);
+                            transitionTo(mDisconnectingErrorCreatingConnection);
                             break;
-                        case ERR_Other:
-                            // Request the failure cause and process in this state
+                        case ERR_GetLastErrorFromRil:
+                            // Request failed and this is an old RIL
                             phone.mCM.getLastDataCallFailCause(
                                     obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
                             break;
+                        case ERR_RilError:
+                            // Request failed and mFailCause has the reason
+                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
+                            transitionTo(mInactiveState);
+                            break;
                         case ERR_Stale:
                             // Request is stale, ignore.
                             break;
@@ -735,7 +674,7 @@
                         if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE");
                         if (ar.exception == null) {
                             int rilFailCause = ((int[]) (ar.result))[0];
-                            cause = getFailCauseFromRequest(rilFailCause);
+                            cause = FailCause.fromInt(rilFailCause);
                         }
                         // Transition to inactive but send notifications after
                         // we've entered the mInactive state.
@@ -852,10 +791,9 @@
     private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
 
     /**
-     * The state machine is disconnecting after a bad dns setup
-     * was found in mInactivatingState.
+     * The state machine is disconnecting after an creating a connection.
      */
-    private class DcDisconnectingBadDnsState extends HierarchicalState {
+    private class DcDisconnectionErrorCreatingConnection extends HierarchicalState {
         @Override protected boolean processMessage(Message msg) {
             boolean retVal;
 
@@ -864,27 +802,38 @@
                     AsyncResult ar = (AsyncResult) msg.obj;
                     ConnectionParams cp = (ConnectionParams) ar.userObj;
                     if (cp.tag == mTag) {
-                        if (DBG) log("DcDisconnectingBadDnsState msg.what=EVENT_DEACTIVATE_DONE");
+                        if (DBG) {
+                            log("DcDisconnectionErrorCreatingConnection" +
+                                " msg.what=EVENT_DEACTIVATE_DONE");
+                        }
+
                         // Transition to inactive but send notifications after
                         // we've entered the mInactive state.
-                        mInactiveState.setEnterNotificationParams(cp, FailCause.UNKNOWN);
+                        mInactiveState.setEnterNotificationParams(cp,
+                                FailCause.UNACCEPTABLE_NETWORK_PARAMETER);
                         transitionTo(mInactiveState);
                     } else {
-                        if (DBG) log("DcDisconnectingBadDnsState EVENT_DEACTIVE_DONE stale dp.tag="
-                                + cp.tag + ", mTag=" + mTag);
+                        if (DBG) {
+                            log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" +
+                                    " stale dp.tag=" + cp.tag + ", mTag=" + mTag);
+                        }
                     }
                     retVal = true;
                     break;
 
                 default:
-                    if (DBG) log("DcDisconnectingBadDnsState not handled msg.what=" + msg.what);
+                    if (DBG) {
+                        log("DcDisconnectionErrorCreatingConnection not handled msg.what="
+                                + msg.what);
+                    }
                     retVal = false;
                     break;
             }
             return retVal;
         }
     }
-    private DcDisconnectingBadDnsState mDisconnectingBadDnsState = new DcDisconnectingBadDnsState();
+    private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
+                new DcDisconnectionErrorCreatingConnection();
 
     // ******* public interface
 
diff --git a/telephony/java/com/android/internal/telephony/EventLogTags.logtags b/telephony/java/com/android/internal/telephony/EventLogTags.logtags
index b06c27d..9be7b80 100644
--- a/telephony/java/com/android/internal/telephony/EventLogTags.logtags
+++ b/telephony/java/com/android/internal/telephony/EventLogTags.logtags
@@ -53,3 +53,6 @@
 
 # CDMA service state transition
 50116 cdma_service_state_change (oldState|1|5), (oldDataState|1|5), (newState|1|5), (newDataState|1|5)
+
+# Bad IP address
+50117 bad_ip_address (ip_address|3)
diff --git a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl b/telephony/java/com/android/internal/telephony/OperatorInfo.aidl
similarity index 78%
rename from telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl
rename to telephony/java/com/android/internal/telephony/OperatorInfo.aidl
index d88d0b7..3ded8dd 100644
--- a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.aidl
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony.gsm;
+package com.android.internal.telephony;
 
 /**
- * Used to indicate that the NetworkInfo object is parcelable to aidl.
- * This is a simple effort to make NetworkInfo parcelable rather than
+ * Used to indicate that the OperatorInfo object is parcelable to aidl.
+ * This is a simple effort to make OperatorInfo parcelable rather than
  * trying to make the conventional containing object (AsyncResult),
  * implement parcelable.  This functionality is needed for the
  * NetworkQueryService to fix 1128695
  */
-parcelable NetworkInfo;
+parcelable OperatorInfo;
diff --git a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
similarity index 78%
rename from telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java
rename to telephony/java/com/android/internal/telephony/OperatorInfo.java
index 04fd13e..1999cb3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony.gsm;
+package com.android.internal.telephony;
 
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -22,7 +22,7 @@
 /**
  * {@hide}
  */
-public class NetworkInfo implements Parcelable {
+public class OperatorInfo implements Parcelable {
     public enum State {
         UNKNOWN,
         AVAILABLE,
@@ -30,11 +30,11 @@
         FORBIDDEN;
     }
 
-    String operatorAlphaLong;
-    String operatorAlphaShort;
-    String operatorNumeric;
+    private String operatorAlphaLong;
+    private String operatorAlphaShort;
+    private String operatorNumeric;
 
-    State state = State.UNKNOWN;
+    private State state = State.UNKNOWN;
 
 
     public String
@@ -57,7 +57,7 @@
         return state;
     }
 
-    NetworkInfo(String operatorAlphaLong,
+    OperatorInfo(String operatorAlphaLong,
                 String operatorAlphaShort,
                 String operatorNumeric,
                 State state) {
@@ -70,7 +70,7 @@
     }
 
 
-    public NetworkInfo(String operatorAlphaLong,
+    public OperatorInfo(String operatorAlphaLong,
                 String operatorAlphaShort,
                 String operatorNumeric,
                 String stateString) {
@@ -98,7 +98,7 @@
 
 
     public String toString() {
-        return "NetworkInfo " + operatorAlphaLong
+        return "OperatorInfo " + operatorAlphaLong
                 + "/" + operatorAlphaShort
                 + "/" + operatorNumeric
                 + "/" + state;
@@ -106,7 +106,7 @@
 
     /**
      * Parcelable interface implemented below.
-     * This is a simple effort to make NetworkInfo parcelable rather than
+     * This is a simple effort to make OperatorInfo parcelable rather than
      * trying to make the conventional containing object (AsyncResult),
      * implement parcelable.  This functionality is needed for the
      * NetworkQueryService to fix 1128695.
@@ -118,7 +118,7 @@
 
     /**
      * Implement the Parcelable interface.
-     * Method to serialize a NetworkInfo object.
+     * Method to serialize a OperatorInfo object.
      */
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(operatorAlphaLong);
@@ -129,21 +129,21 @@
 
     /**
      * Implement the Parcelable interface
-     * Method to deserialize a NetworkInfo object, or an array thereof.
+     * Method to deserialize a OperatorInfo object, or an array thereof.
      */
-    public static final Creator<NetworkInfo> CREATOR =
-        new Creator<NetworkInfo>() {
-            public NetworkInfo createFromParcel(Parcel in) {
-                NetworkInfo netInfo = new NetworkInfo(
+    public static final Creator<OperatorInfo> CREATOR =
+        new Creator<OperatorInfo>() {
+            public OperatorInfo createFromParcel(Parcel in) {
+                OperatorInfo opInfo = new OperatorInfo(
                         in.readString(), /*operatorAlphaLong*/
                         in.readString(), /*operatorAlphaShort*/
                         in.readString(), /*operatorNumeric*/
                         (State) in.readSerializable()); /*state*/
-                return netInfo;
+                return opInfo;
             }
 
-            public NetworkInfo[] newArray(int size) {
-                return new NetworkInfo[size];
+            public OperatorInfo[] newArray(int size) {
+                return new OperatorInfo[size];
             }
         };
 }
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index d0b5540..e2896d9b 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -27,7 +27,6 @@
 import android.telephony.SignalStrength;
 
 import com.android.internal.telephony.DataConnection;
-import com.android.internal.telephony.gsm.NetworkInfo;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
 import java.util.List;
@@ -1056,7 +1055,7 @@
      * one of the following members:.<p>
      *<ul>
      * <li><code>response.obj.result</code> will be a <code>List</code> of
-     * <code>com.android.internal.telephony.gsm.NetworkInfo</code> objects, or</li>
+     * <code>OperatorInfo</code> objects, or</li>
      * <li><code>response.obj.exception</code> will be set with an exception
      * on failure.</li>
      * </ul>
@@ -1070,8 +1069,7 @@
      * @param response The message to dispatch when the network selection
      * is complete.
      *
-     * @see #selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo,
-     * android.os.Message )
+     * @see #selectNetworkManually(OperatorInfo, android.os.Message )
      */
     void setNetworkSelectionModeAutomatic(Message response);
 
@@ -1083,7 +1081,7 @@
      *
      * @see #setNetworkSelectionModeAutomatic(Message)
      */
-    void selectNetworkManually(NetworkInfo network,
+    void selectNetworkManually(OperatorInfo network,
                             Message response);
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 219efbb..15b23bb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -32,7 +32,6 @@
 
 import com.android.internal.telephony.cdma.CDMAPhone;
 import com.android.internal.telephony.gsm.GSMPhone;
-import com.android.internal.telephony.gsm.NetworkInfo;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
 import java.util.List;
@@ -550,7 +549,7 @@
         mActivePhone.setNetworkSelectionModeAutomatic(response);
     }
 
-    public void selectNetworkManually(NetworkInfo network, Message response) {
+    public void selectNetworkManually(OperatorInfo network, Message response) {
         mActivePhone.selectNetworkManually(network, response);
     }
 
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 7506bb1..21c88f1 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -50,12 +50,12 @@
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.DataCallState;
-import com.android.internal.telephony.gsm.NetworkInfo;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.IccCardApplication;
 import com.android.internal.telephony.IccCardStatus;
 import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.SmsResponse;
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
@@ -2139,7 +2139,7 @@
             case RIL_REQUEST_DTMF: ret =  responseVoid(p); break;
             case RIL_REQUEST_SEND_SMS: ret =  responseSMS(p); break;
             case RIL_REQUEST_SEND_SMS_EXPECT_MORE: ret =  responseSMS(p); break;
-            case RIL_REQUEST_SETUP_DATA_CALL: ret =  responseStrings(p); break;
+            case RIL_REQUEST_SETUP_DATA_CALL: ret =  responseSetupDataCall(p); break;
             case RIL_REQUEST_SIM_IO: ret =  responseICC_IO(p); break;
             case RIL_REQUEST_SEND_USSD: ret =  responseVoid(p); break;
             case RIL_REQUEST_CANCEL_USSD: ret =  responseVoid(p); break;
@@ -2160,7 +2160,7 @@
             case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret =  responseInts(p); break;
             case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret =  responseVoid(p); break;
             case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret =  responseNetworkInfos(p); break;
+            case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret =  responseOperatorInfos(p); break;
             case RIL_REQUEST_DTMF_START: ret =  responseVoid(p); break;
             case RIL_REQUEST_DTMF_STOP: ret =  responseVoid(p); break;
             case RIL_REQUEST_BASEBAND_VERSION: ret =  responseString(p); break;
@@ -2897,37 +2897,95 @@
         return response;
     }
 
-    private Object
-    responseDataCallList(Parcel p) {
-        int num;
-        ArrayList<DataCallState> response;
+    private DataCallState getDataCallState(Parcel p, int version) {
+        DataCallState dataCall = new DataCallState();
 
-        num = p.readInt();
-        response = new ArrayList<DataCallState>(num);
-
-        for (int i = 0; i < num; i++) {
-            DataCallState dataCall = new DataCallState();
-
+        dataCall.version = version;
+        if (version < 5) {
             dataCall.cid = p.readInt();
             dataCall.active = p.readInt();
             dataCall.type = p.readString();
-            dataCall.apn = p.readString();
-            String address = p.readString();
-            if (address != null) {
-                address = address.split(" ")[0];
+            p.readString(); // Ignore apn
+            String addresses = p.readString();
+            if (addresses != null) {
+                dataCall.addresses = addresses.split(" ");
             }
-            dataCall.address = address;
+        } else {
+            dataCall.status = p.readInt();
+            dataCall.cid = p.readInt();
+            dataCall.active = p.readInt();
+            dataCall.type = p.readString();
+            dataCall.ifname = p.readString();
+            String addresses = p.readString();
+            if (addresses != null) {
+                dataCall.addresses = addresses.split(" ");
+            }
+            String dnses = p.readString();
+            if (addresses != null) {
+                dataCall.dnses = dnses.split(" ");
+            }
+        }
+        return dataCall;
+    }
 
-            response.add(dataCall);
+    private Object
+    responseDataCallList(Parcel p) {
+        ArrayList<DataCallState> response;
+
+        int ver = p.readInt();
+        int num = p.readInt();
+        Log.d(LOG_TAG, "responseDataCallList ver=" + ver + " num=" + num);
+
+        response = new ArrayList<DataCallState>(num);
+        for (int i = 0; i < num; i++) {
+            response.add(getDataCallState(p, ver));
         }
 
         return response;
     }
 
     private Object
-    responseNetworkInfos(Parcel p) {
+    responseSetupDataCall(Parcel p) {
+        int ver = p.readInt();
+        int num = p.readInt();
+        Log.d(LOG_TAG, "responseSetupDataCall ver=" + ver + " num=" + num);
+
+        DataCallState dataCall;
+
+        if (ver < 5) {
+            if (num != 3) {
+                throw new RuntimeException(
+                        "RIL_REQUEST_SETUP_DATA_CALL response expecting 3 strings got " + num);
+            }
+            dataCall = new DataCallState();
+            dataCall.cid = Integer.parseInt(p.readString());
+            dataCall.ifname = p.readString();
+            if (dataCall.ifname == null) {
+                throw new RuntimeException(
+                        "RIL_REQUEST_SETUP_DATA_CALL response ifname");
+            }
+            String addresses = p.readString();
+            if (addresses == null) {
+                throw new RuntimeException(
+                "RIL_REQUEST_SETUP_DATA_CALL response no addresses");
+            }
+            dataCall.addresses = addresses.split(" ");
+        } else {
+            if (num != 1) {
+                throw new RuntimeException(
+                        "RIL_REQUEST_SETUP_DATA_CALL response expecting 1 RIL_Data_Call_response_v5"
+                        + " got " + num);
+            }
+            dataCall = getDataCallState(p, ver);
+        }
+
+        return dataCall;
+    }
+
+    private Object
+    responseOperatorInfos(Parcel p) {
         String strings[] = (String [])responseStrings(p);
-        ArrayList<NetworkInfo> ret;
+        ArrayList<OperatorInfo> ret;
 
         if (strings.length % 4 != 0) {
             throw new RuntimeException(
@@ -2935,11 +2993,11 @@
                 + strings.length + " strings, expected multible of 4");
         }
 
-        ret = new ArrayList<NetworkInfo>(strings.length / 4);
+        ret = new ArrayList<OperatorInfo>(strings.length / 4);
 
         for (int i = 0 ; i < strings.length ; i += 4) {
             ret.add (
-                new NetworkInfo(
+                new OperatorInfo(
                     strings[i+0],
                     strings[i+1],
                     strings[i+2],
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index a1d5121..3131ebe 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -56,6 +56,7 @@
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneNotifier;
@@ -483,7 +484,7 @@
     }
 
     public void
-    selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo network,
+    selectNetworkManually(OperatorInfo network,
             Message response) {
         Log.e(LOG_TAG, "selectNetworkManually: not possible in CDMA");
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index 1b2c918..1a0dbc2 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -20,6 +20,7 @@
 import android.util.Log;
 
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RetryManager;
@@ -31,13 +32,6 @@
 
     private static final String LOG_TAG = "CDMA";
 
-    /** Fail cause of last Data Call activate from RIL_LastDataCallActivateFailCause */
-    private final static int PS_NET_DOWN_REASON_OPERATOR_DETERMINED_BARRING         = 8;
-    private final static int PS_NET_DOWN_REASON_AUTH_FAILED                         = 29;
-    private final static int PS_NET_DOWN_REASON_OPTION_NOT_SUPPORTED                = 32;
-    private final static int PS_NET_DOWN_REASON_OPTION_UNSUBSCRIBED                 = 33;
-
-
     // ***** Constructor
     private CdmaDataConnection(CDMAPhone phone, String name, RetryManager rm) {
         super(phone, name, rm);
@@ -104,29 +98,6 @@
     }
 
     @Override
-    protected FailCause getFailCauseFromRequest(int rilCause) {
-        FailCause cause;
-
-        switch (rilCause) {
-            case PS_NET_DOWN_REASON_OPERATOR_DETERMINED_BARRING:
-                cause = FailCause.OPERATOR_BARRED;
-                break;
-            case PS_NET_DOWN_REASON_AUTH_FAILED:
-                cause = FailCause.USER_AUTHENTICATION;
-                break;
-            case PS_NET_DOWN_REASON_OPTION_NOT_SUPPORTED:
-                cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED;
-                break;
-            case PS_NET_DOWN_REASON_OPTION_UNSUBSCRIBED:
-                cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED;
-                break;
-            default:
-                cause = FailCause.UNKNOWN;
-        }
-        return cause;
-    }
-
-    @Override
     protected boolean isDnsOk(String[] domainNameServers) {
         if ((NULL_IP.equals(domainNameServers[0])
                 && NULL_IP.equals(domainNameServers[1])
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index e8ad0f2..c17197e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -62,6 +62,7 @@
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneNotifier;
@@ -1025,19 +1026,19 @@
     }
 
     public void
-    selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo network,
+    selectNetworkManually(OperatorInfo network,
             Message response) {
         // wrap the response message in our own message along with
         // the operator's id.
         NetworkSelectMessage nsm = new NetworkSelectMessage();
         nsm.message = response;
-        nsm.operatorNumeric = network.operatorNumeric;
-        nsm.operatorAlphaLong = network.operatorAlphaLong;
+        nsm.operatorNumeric = network.getOperatorNumeric();
+        nsm.operatorAlphaLong = network.getOperatorAlphaLong();
 
         // get the message
         Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm);
 
-        mCM.setNetworkSelectionModeManual(network.operatorNumeric, msg);
+        mCM.setNetworkSelectionModeManual(network.getOperatorNumeric(), msg);
     }
 
     public void
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index b947020..1d60bda 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -33,24 +33,6 @@
 
     private static final String LOG_TAG = "GSM";
 
-    /** Fail cause of last PDP activate, from RIL_LastPDPActivateFailCause */
-    private static final int PDP_FAIL_OPERATOR_BARRED = 0x08;
-    private static final int PDP_FAIL_INSUFFICIENT_RESOURCES = 0x1A;
-    private static final int PDP_FAIL_MISSING_UKNOWN_APN = 0x1B;
-    private static final int PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C;
-    private static final int PDP_FAIL_USER_AUTHENTICATION = 0x1D;
-    private static final int PDP_FAIL_ACTIVATION_REJECT_GGSN = 0x1E;
-    private static final int PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
-    private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20;
-    private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21;
-    private static final int PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22;
-    private static final int PDP_FAIL_NSAPI_IN_USE      = 0x23;
-    private static final int PDP_FAIL_PROTOCOL_ERRORS   = 0x6F;
-    private static final int PDP_FAIL_ERROR_UNSPECIFIED = 0xffff;
-
-    private static final int PDP_FAIL_REGISTRATION_FAIL = -1;
-    private static final int PDP_FAIL_GPRS_REGISTRATION_FAIL = -2;
-
     //***** Instance Variables
     private ApnSetting apn;
 
@@ -132,62 +114,6 @@
     }
 
     @Override
-    protected FailCause getFailCauseFromRequest(int rilCause) {
-        FailCause cause;
-
-        switch (rilCause) {
-            case PDP_FAIL_OPERATOR_BARRED:
-                cause = FailCause.OPERATOR_BARRED;
-                break;
-            case PDP_FAIL_INSUFFICIENT_RESOURCES:
-                cause = FailCause.INSUFFICIENT_RESOURCES;
-                break;
-            case PDP_FAIL_MISSING_UKNOWN_APN:
-                cause = FailCause.MISSING_UNKNOWN_APN;
-                break;
-            case PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE:
-                cause = FailCause.UNKNOWN_PDP_ADDRESS;
-                break;
-            case PDP_FAIL_USER_AUTHENTICATION:
-                cause = FailCause.USER_AUTHENTICATION;
-                break;
-            case PDP_FAIL_ACTIVATION_REJECT_GGSN:
-                cause = FailCause.ACTIVATION_REJECT_GGSN;
-                break;
-            case PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED:
-                cause = FailCause.ACTIVATION_REJECT_UNSPECIFIED;
-                break;
-            case PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER:
-                cause = FailCause.SERVICE_OPTION_OUT_OF_ORDER;
-                break;
-            case PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED:
-                cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED;
-                break;
-            case PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED:
-                cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED;
-                break;
-            case PDP_FAIL_NSAPI_IN_USE:
-                cause = FailCause.NSAPI_IN_USE;
-                break;
-            case PDP_FAIL_PROTOCOL_ERRORS:
-                cause = FailCause.PROTOCOL_ERRORS;
-                break;
-            case PDP_FAIL_ERROR_UNSPECIFIED:
-                cause = FailCause.UNKNOWN;
-                break;
-            case PDP_FAIL_REGISTRATION_FAIL:
-                cause = FailCause.REGISTRATION_FAIL;
-                break;
-            case PDP_FAIL_GPRS_REGISTRATION_FAIL:
-                cause = FailCause.GPRS_REGISTRATION_FAIL;
-                break;
-            default:
-                cause = FailCause.UNKNOWN;
-        }
-        return cause;
-    }
-
-    @Override
     protected boolean isDnsOk(String[] domainNameServers) {
         if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
                 && !((GSMPhone) phone).isDnsCheckDisabled()) {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
index db9e35a..9dfc015 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -38,6 +38,7 @@
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.MmiCode;
+import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneNotifier;
@@ -347,7 +348,7 @@
     }
 
     public void selectNetworkManually(
-            com.android.internal.telephony.gsm.NetworkInfo network,
+            OperatorInfo network,
             Message response) {
     }
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 4d0acdd..42a27d1 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -44,10 +44,13 @@
 import android.net.DhcpInfo;
 import android.net.NetworkUtils;
 import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkUtils;
 import android.net.LinkProperties;
 import android.net.wifi.NetworkUpdateResult;
 import android.net.wifi.WpsResult.Status;
+import android.net.InterfaceConfiguration;
 import android.os.Binder;
 import android.os.Message;
 import android.os.IBinder;
@@ -73,7 +76,7 @@
 import com.android.internal.util.HierarchicalState;
 import com.android.internal.util.HierarchicalStateMachine;
 
-
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -465,6 +468,20 @@
         mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
 
         mContext.registerReceiver(
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+
+                    ArrayList<String> available = intent.getStringArrayListExtra(
+                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+                    ArrayList<String> active = intent.getStringArrayListExtra(
+                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
+                    updateTetherState(available, active);
+
+                }
+            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
+        mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
@@ -972,6 +989,52 @@
      * Internal private functions
      ********************************************************/
 
+    private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
+
+        boolean wifiTethered = false;
+        boolean wifiAvailable = false;
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        if (mCm == null) {
+            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        }
+
+        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
+
+        for (String intf : available) {
+            for (String regex : wifiRegexs) {
+                if (intf.matches(regex)) {
+
+                    InterfaceConfiguration ifcg = null;
+                    try {
+                        ifcg = service.getInterfaceConfig(intf);
+                        if (ifcg != null) {
+                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
+                            ifcg.addr = InetAddress.getByName("192.168.43.1");
+                            ifcg.mask = InetAddress.getByName("255.255.255.0");
+                            ifcg.interfaceFlags = "[up]";
+
+                            service.setInterfaceConfig(intf, ifcg);
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error configuring interface " + intf + ", :" + e);
+                        setWifiApEnabled(null, false);
+                        return;
+                    }
+
+                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                        Log.e(TAG, "Error tethering on " + intf);
+                        setWifiApEnabled(null, false);
+                        return;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
     /**
      * Set the country code from the system setting value, if any.
      */
@@ -2467,14 +2530,24 @@
                 mDhcpThread.start();
             } else {
                 DhcpInfo dhcpInfo = WifiConfigStore.getIpConfiguration(mLastNetworkId);
-                if (NetworkUtils.configureInterface(mInterfaceName, dhcpInfo)) {
+                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService netd = INetworkManagementService.Stub.asInterface(b);
+                InterfaceConfiguration ifcg = new InterfaceConfiguration();
+                ifcg.interfaceFlags = "[up]";
+                ifcg.addr = NetworkUtils.intToInetAddress(dhcpInfo.ipAddress);
+                ifcg.mask = NetworkUtils.intToInetAddress(dhcpInfo.netmask);
+                try {
+                    netd.setInterfaceConfig(mInterfaceName, ifcg);
                     Log.v(TAG, "Static IP configuration succeeded");
                     synchronized (mDhcpInfo) {
                         mDhcpInfo = dhcpInfo;
                     }
                     sendMessage(CMD_IP_CONFIG_SUCCESS);
-                } else {
-                    Log.v(TAG, "Static IP configuration failed");
+                } catch (RemoteException re) {
+                    Log.v(TAG, "Static IP configuration failed: " + re);
+                    sendMessage(CMD_IP_CONFIG_FAILURE);
+                } catch (IllegalStateException e) {
+                    Log.v(TAG, "Static IP configuration failed: " + e);
                     sendMessage(CMD_IP_CONFIG_FAILURE);
                 }
             }
@@ -2818,6 +2891,14 @@
                 case CMD_STOP_AP:
                     Log.d(TAG,"Stopping Soft AP");
                     setWifiApState(WIFI_AP_STATE_DISABLING);
+
+                    if (mCm == null) {
+                        mCm = (ConnectivityManager) mContext.getSystemService(
+                                Context.CONNECTIVITY_SERVICE);
+                    }
+                    if (mCm.untether(SOFTAP_IFACE) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                        Log.e(TAG, "Untether initiate failed!");
+                    }
                     try {
                         nwService.stopAccessPoint();
                     } catch(Exception e) {