Merge "Fix 4027057: Improve resolution of RecentApps thumbnail images." into honeycomb-mr1
diff --git a/api/current.xml b/api/current.xml
index 4996e6a..cbccf93 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -95457,6 +95457,32 @@
  visibility="public"
 >
 </method>
+<method name="hasPermission"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.hardware.usb.UsbDevice">
+</parameter>
+</method>
+<method name="hasPermission"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accessory" type="android.hardware.usb.UsbAccessory">
+</parameter>
+</method>
 <method name="isFunctionEnabled"
  return="boolean"
  abstract="false"
@@ -95509,6 +95535,36 @@
 <parameter name="device" type="android.hardware.usb.UsbDevice">
 </parameter>
 </method>
+<method name="requestPermission"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.hardware.usb.UsbDevice">
+</parameter>
+<parameter name="pi" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="requestPermission"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accessory" type="android.hardware.usb.UsbAccessory">
+</parameter>
+<parameter name="pi" type="android.app.PendingIntent">
+</parameter>
+</method>
 <field name="ACTION_USB_ACCESSORY_ATTACHED"
  type="java.lang.String"
  transient="false"
@@ -95586,6 +95642,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_PERMISSION_GRANTED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;permission&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="USB_CONFIGURATION"
  type="java.lang.String"
  transient="false"
@@ -244856,6 +244923,25 @@
 <parameter name="realm" type="java.lang.String">
 </parameter>
 </method>
+<method name="onReceivedLoginRequest"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.webkit.WebView">
+</parameter>
+<parameter name="realm" type="java.lang.String">
+</parameter>
+<parameter name="account" type="java.lang.String">
+</parameter>
+<parameter name="args" type="java.lang.String">
+</parameter>
+</method>
 <method name="onReceivedSslError"
  return="void"
  abstract="false"
@@ -267304,7 +267390,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 539e946..cc1f81c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -401,10 +401,10 @@
                     return new UiModeManager();
                 }});
 
-        registerService(USB_SERVICE, new StaticServiceFetcher() {
-                public Object createStaticService() {
+        registerService(USB_SERVICE, new ServiceFetcher() {
+                public Object createService(ContextImpl ctx) {
                     IBinder b = ServiceManager.getService(USB_SERVICE);
-                    return new UsbManager(IUsbManager.Stub.asInterface(b));
+                    return new UsbManager(ctx, IUsbManager.Stub.asInterface(b));
                 }});
 
         registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 440cb54..efe2633 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1150,8 +1150,12 @@
      * fail (most commonly returning {@link #ENCRYPTION_STATUS_ACTIVE}).
      *
      * <p>This policy controls encryption of the secure (application data) storage area.  Data
-     * written to other areas (e.g. the directory returned by
-     * {@link android.os.Environment#getExternalStorageDirectory()} may or may not be encrypted.
+     * written to other storage areas may or may not be encrypted, and this policy does not require
+     * or control the encryption of any other storage areas.
+     * There is one exception:  If {@link android.os.Environment#isExternalStorageEmulated()} is
+     * {@code true}, then the directory returned by
+     * {@link android.os.Environment#getExternalStorageDirectory()} must be written to disk
+     * within the encrypted storage area.
      *
      * <p>Important Note:  On some devices, it is possible to encrypt storage without requiring
      * the user to create a device PIN or Password.  In this case, the storage is encrypted, but
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index dd4096b..2111cce 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -2051,8 +2051,8 @@
         if (rv.length == 4) {
             Q[0] = rv[3];
         } else {
-            //In this case, the w component of the quaternion is known to be a positive number
-            Q[0] = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]);
+            Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2];
+            Q[0] = (Q[0] > 0) ? (float)Math.sqrt(Q[0]) : 0;
         }
         Q[1] = rv[0];
         Q[2] = rv[1];
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index be65bdb..495fa21 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.usb;
 
+import android.app.PendingIntent;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.os.Bundle;
@@ -50,6 +51,25 @@
      */
     void setAccessoryPackage(in UsbAccessory accessory, String packageName);
 
+    /* Returns true if the caller has permission to access the device. */
+    boolean hasDevicePermission(in UsbDevice device);
+
+    /* Returns true if the caller has permission to access the accessory. */
+    boolean hasAccessoryPermission(in UsbAccessory accessory);
+
+    /* Requests permission for the given package to access the device.
+     * Will display a system dialog to query the user if permission
+     * had not already been given.
+     */
+    void requestDevicePermission(in UsbDevice device, String packageName, in PendingIntent pi);
+
+    /* Requests permission for the given package to access the accessory.
+     * Will display a system dialog to query the user if permission
+     * had not already been given. Result is returned via pi.
+     */
+    void requestAccessoryPermission(in UsbAccessory accessory, String packageName,
+            in PendingIntent pi);
+
     /* Grants permission for the given UID to access the device */
     void grantDevicePermission(in UsbDevice device, int uid);
 
@@ -57,8 +77,8 @@
     void grantAccessoryPermission(in UsbAccessory accessory, int uid);
 
     /* Returns true if the USB manager has default preferences or permissions for the package */
-    boolean hasDefaults(String packageName, int uid);
+    boolean hasDefaults(String packageName);
 
     /* Clears default preferences and permissions for the package */
-    oneway void clearDefaults(String packageName, int uid);
+    oneway void clearDefaults(String packageName);
 }
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index 6cd9178..7d66caa 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -109,6 +109,14 @@
     }
 
     @Override
+    public int hashCode() {
+        return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
+                (mModel == null ? 0 : mModel.hashCode()) ^
+                (mType == null ? 0 : mType.hashCode()) ^
+                (mVersion == null ? 0 : mVersion.hashCode()));
+    }
+
+    @Override
     public String toString() {
         return "UsbAccessory[mManufacturer=" + mManufacturer +
                             ", mModel=" + mModel +
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 37bd82b..39254b38 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -270,6 +270,11 @@
     }
 
     @Override
+    public int hashCode() {
+        return mName.hashCode();
+    }
+
+    @Override
     public String toString() {
         return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId +
                 ",mProductId=" + mProductId + ",mClass=" + mClass +
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 6683179..e80c744 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -17,6 +17,8 @@
 
 package android.hardware.usb;
 
+import android.app.PendingIntent;
+import android.content.Context;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -98,7 +100,7 @@
      *
      * This intent is sent when a USB accessory is detached.
      * <ul>
-     * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
+     * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory}
      * for the attached accessory that was detached
      * </ul>
      */
@@ -176,12 +178,22 @@
      */
     public static final String EXTRA_ACCESSORY = "accessory";
 
-    private IUsbManager mService;
+    /**
+     * Name of extra added to the {@link android.app.PendingIntent}
+     * passed into {@link #requestPermission(UsbDevice, PendingIntent)}
+     * or {@link #requestPermission(UsbAccessory, PendingIntent)}
+     * containing a boolean value indicating whether the user granted permission or not.
+     */
+    public static final String EXTRA_PERMISSION_GRANTED = "permission";
+
+    private final Context mContext;
+    private final IUsbManager mService;
 
     /**
      * {@hide}
      */
-    public UsbManager(IUsbManager service) {
+    public UsbManager(Context context, IUsbManager service) {
+        mContext = context;
         mService = service;
     }
 
@@ -245,7 +257,7 @@
                 return new UsbAccessory[] { accessory };
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in getAccessoryList" , e);
+            Log.e(TAG, "RemoteException in getAccessoryList", e);
             return null;
         }
     }
@@ -260,11 +272,99 @@
         try {
             return mService.openAccessory(accessory);
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in openAccessory" , e);
+            Log.e(TAG, "RemoteException in openAccessory", e);
             return null;
         }
     }
 
+    /**
+     * Returns true if the caller has permission to access the device.
+     * Permission might have been granted temporarily via
+     * {@link #requestPermission(UsbDevice, PendingIntent)} or
+     * by the user choosing the caller as the default application for the device.
+     *
+     * @param device to check permissions for
+     * @return true if caller has permission
+     */
+    public boolean hasPermission(UsbDevice device) {
+        try {
+            return mService.hasDevicePermission(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in hasPermission", e);
+            return false;
+        }
+    }
+
+    /**
+     * Returns true if the caller has permission to access the accessory.
+     * Permission might have been granted temporarily via
+     * {@link #requestPermission(UsbAccessory, PendingIntent)} or
+     * by the user choosing the caller as the default application for the accessory.
+     *
+     * @param accessory to check permissions for
+     * @return true if caller has permission
+     */
+    public boolean hasPermission(UsbAccessory accessory) {
+        try {
+            return mService.hasAccessoryPermission(accessory);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in hasPermission", e);
+            return false;
+        }
+    }
+
+    /**
+     * Requests temporary permission for the given package to access the device.
+     * This may result in a system dialog being displayed to the user
+     * if permission had not already been granted.
+     * Success or failure is returned via the {@link android.app.PendingIntent} pi.
+     * If successful, this grants the caller permission to access the device only
+     * until the device is disconnected.
+     *
+     * The following extras will be added to pi:
+     * <ul>
+     * <li> {@link #EXTRA_DEVICE} containing the device passed into this call
+     * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
+     * permission was granted by the user
+     * </ul>
+     *
+     * @param device to request permissions for
+     * @param pi PendingIntent for returning result
+     */
+    public void requestPermission(UsbDevice device, PendingIntent pi) {
+        try {
+            mService.requestDevicePermission(device, mContext.getPackageName(), pi);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in requestPermission", e);
+        }
+    }
+
+    /**
+     * Requests temporary permission for the given package to access the accessory.
+     * This may result in a system dialog being displayed to the user
+     * if permission had not already been granted.
+     * Success or failure is returned via the {@link android.app.PendingIntent} pi.
+     * If successful, this grants the caller permission to access the device only
+     * until the device is disconnected.
+     *
+     * The following extras will be added to pi:
+     * <ul>
+     * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call
+     * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
+     * permission was granted by the user
+     * </ul>
+     *
+     * @param accessory to request permissions for
+     * @param pi PendingIntent for returning result
+     */
+    public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
+        try {
+            mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in requestPermission", e);
+        }
+    }
+
     private static File getFunctionEnableFile(String function) {
         return new File("/sys/class/usb_composite/" + function + "/enable");
     }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index ec5030c..e308c2c 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -423,9 +423,16 @@
 
     /**
      * Returns whether the device has an external storage device which is
-     * emulated. If true, the device does not have real external storage
-     * and certain system services such as the package manager use this
+     * emulated. If true, the device does not have real external storage, and the directory
+     * returned by {@link #getExternalStorageDirectory()} will be allocated using a portion of
+     * the internal storage system.
+     *
+     * <p>Certain system services, such as the package manager, use this
      * to determine where to install an application.
+     *
+     * <p>Emulated external storage may also be encrypted - see
+     * {@link android.app.admin.DevicePolicyManager#setStorageEncryption(
+     * android.content.ComponentName, boolean)} for additional details.
      */
     public static boolean isExternalStorageEmulated() {
         if (mIsExternalStorageEmulated == null) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index c078c08..0cf7ae6 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -609,7 +609,7 @@
                         DisplayList displayList = view.getDisplayList();
                         if (displayList != null) {
                             if (canvas.drawDisplayList(displayList, mRedrawClip)) {
-                                if (mRedrawClip.isEmpty()) {
+                                if (mRedrawClip.isEmpty() || view.getParent() == null) {
                                     view.invalidate();
                                 } else {
                                     view.getParent().invalidateChild(view, mRedrawClip);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6ef680b..f9692da 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3680,7 +3680,7 @@
                     // flag coming from the child that initiated the invalidate
                     if (view != null) {
                         if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
-                                view.getSolidColor() == 0 && !view.isOpaque()) {
+                                view.getSolidColor() == 0) {
                             opaqueFlag = DIRTY;
                         }
                         if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index ab4bfe1..c7a7374 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1237,11 +1237,17 @@
         }
     }
 
-
     /*package*/ SearchBox getSearchBox() {
         return mSearchBox;
     }
 
+    /**
+     * Called by JNI when processing the X-Auto-Login header.
+     */
+    private void autoLogin(String realm, String account, String args) {
+        mCallbackProxy.onReceivedLoginRequest(realm, account, args);
+    }
+
     //==========================================================================
     // native functions
     //==========================================================================
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index c66d701..23fd12d 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -117,6 +117,7 @@
     private static final int AUTH_CREDENTIALS                    = 137;
     private static final int SET_INSTALLABLE_WEBAPP              = 138;
     private static final int NOTIFY_SEARCHBOX_LISTENERS          = 139;
+    private static final int AUTO_LOGIN                          = 140;
 
     // Message triggered by the client to resume execution
     private static final int NOTIFY                              = 200;
@@ -770,7 +771,7 @@
                             (WebHistoryItem) msg.obj, msg.arg1);
                 }
                 break;
-            case AUTH_CREDENTIALS:
+            case AUTH_CREDENTIALS: {
                 String host = msg.getData().getString("host");
                 String realm = msg.getData().getString("realm");
                 username = msg.getData().getString("username");
@@ -778,6 +779,7 @@
                 mWebView.setHttpAuthUsernamePassword(
                         host, realm, username, password);
                 break;
+            }
             case SET_INSTALLABLE_WEBAPP:
                 if (mWebChromeClient != null) {
                     mWebChromeClient.setInstallableWebApp();
@@ -789,6 +791,17 @@
                 @SuppressWarnings("unchecked")
                 List<String> suggestions = (List<String>) msg.obj;
                 searchBox.handleSuggestions(msg.getData().getString("query"), suggestions);
+                break;
+            case AUTO_LOGIN: {
+                if (mWebViewClient != null) {
+                    String realm = msg.getData().getString("realm");
+                    String account = msg.getData().getString("account");
+                    String args = msg.getData().getString("args");
+                    mWebViewClient.onReceivedLoginRequest(mWebView, realm,
+                            account, args);
+                }
+                break;
+            }
         }
     }
 
@@ -1051,6 +1064,20 @@
         sendMessage(msg);
     }
 
+    void onReceivedLoginRequest(String realm, String account, String args) {
+        // Do an unsynchronized quick check to avoid posting if no callback has
+        // been set.
+        if (mWebViewClient == null) {
+            return;
+        }
+        Message msg = obtainMessage(AUTO_LOGIN);
+        Bundle bundle = msg.getData();
+        bundle.putString("realm", realm);
+        bundle.putString("account", account);
+        bundle.putString("args", args);
+        sendMessage(msg);
+    }
+
     //--------------------------------------------------------------------------
     // DownloadListener functions.
     //--------------------------------------------------------------------------
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index 2dac7e9..3cb5e24 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -31,7 +31,7 @@
     public static final boolean CACHE_MANAGER = false;
     public static final boolean CALLBACK_PROXY = false;
     public static final boolean COOKIE_MANAGER = false;
-    public static final boolean COOKIE_SYNC_MANAGER = true;
+    public static final boolean COOKIE_SYNC_MANAGER = false;
     public static final boolean FRAME_LOADER = false;
     public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
     public static final boolean LOAD_LISTENER = false;
@@ -41,7 +41,7 @@
     public static final boolean URL_UTIL = false;
     public static final boolean WEB_BACK_FORWARD_LIST = false;
     public static final boolean WEB_SETTINGS = false;
-    public static final boolean WEB_SYNC_MANAGER = true;
+    public static final boolean WEB_SYNC_MANAGER = false;
     public static final boolean WEB_TEXT_VIEW = false;
     public static final boolean WEB_VIEW = false;
     public static final boolean WEB_VIEW_CORE = false;
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 28b09519..0f24edc 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -26,14 +26,15 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.ResultReceiver;
 import android.text.BoringLayout.Metrics;
 import android.text.DynamicLayout;
 import android.text.Editable;
 import android.text.InputFilter;
 import android.text.Layout;
-import android.text.Layout.Alignment;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextPaint;
@@ -136,6 +137,23 @@
     // Used to determine whether onFocusChanged was called as a result of
     // calling remove().
     private boolean mInsideRemove;
+    private class MyResultReceiver extends ResultReceiver {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            if (resultCode == InputMethodManager.RESULT_SHOWN
+                    && mWebView != null) {
+                mWebView.revealSelection();
+            }
+        }
+
+        /**
+         * @param handler
+         */
+        public MyResultReceiver(Handler handler) {
+            super(handler);
+        }
+    }
+    private MyResultReceiver mReceiver;
 
     // Types used with setType.  Keep in sync with CachedInput.h
     private static final int NORMAL_TEXT_FIELD = 0;
@@ -184,7 +202,7 @@
                 }
             }
         };
-
+        mReceiver = new MyResultReceiver(mHandler);
     }
 
     public void setAutoFillable(int queryId) {
@@ -362,6 +380,8 @@
         }
     }
 
+    /* package */ ResultReceiver getResultReceiver() { return mReceiver; }
+
     /**
      *  Determine whether this WebTextView currently represents the node
      *  represented by ptr.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1316235..705eefc 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -497,15 +497,6 @@
     // default is not set, the UI will continue handle them.
     private boolean mDeferTouchProcess;
 
-    // Currently, multi-touch events are sent to WebKit first then back to
-    // WebView while single-touch events are handled in WebView first.
-    // So there is a chance that a single-touch move event is handled in WebView
-    // before multi-touch events are finished.
-    // if mIsHandlingMultiTouch is true, which means multi-touch event handling
-    // is not finished, then any single-touch move event will be skipped.
-    // FIXME: send single-touch events to WebKit first then back to WebView.
-    private boolean mIsHandlingMultiTouch = false;
-
     // to avoid interfering with the current touch events, track them
     // separately. Currently no snapping or fling in the deferred process mode
     private int mDeferTouchMode = TOUCH_DONE_MODE;
@@ -4036,15 +4027,10 @@
         }
     }
 
-    void setBaseLayer(int layer, Rect invalRect, boolean showVisualIndciator) {
+    void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator) {
         if (mNativeClass == 0)
             return;
-        if (invalRect == null) {
-            Rect rect = new Rect(0, 0, mContentWidth, mContentHeight);
-            nativeSetBaseLayer(layer, rect, showVisualIndciator);
-        } else {
-            nativeSetBaseLayer(layer, invalRect, showVisualIndciator);
-        }
+        nativeSetBaseLayer(layer, invalRegion, showVisualIndicator);
     }
 
     private void onZoomAnimationStart() {
@@ -4284,7 +4270,7 @@
         if (isTextView) {
             rebuildWebTextView();
             if (inEditingMode()) {
-                imm.showSoftInput(mWebTextView, 0);
+                imm.showSoftInput(mWebTextView, 0, mWebTextView.getResultReceiver());
                 if (zoom) {
                     didUpdateWebTextViewDimensions(INTERSECTS_SCREEN);
                 }
@@ -5479,7 +5465,6 @@
             case MotionEvent.ACTION_DOWN: {
                 mPreventDefault = PREVENT_DEFAULT_NO;
                 mConfirmMove = false;
-                mIsHandlingMultiTouch = false;
                 mInitialHitTestResult = null;
                 if (!mScroller.isFinished()) {
                     // stop the current scroll animation, but if this is
@@ -5571,7 +5556,7 @@
                         ted.mReprocess = mDeferTouchProcess;
                         ted.mNativeLayer = nativeScrollableLayer(
                                 contentX, contentY, ted.mNativeLayerRect, null);
-                        ted.mDontEnqueueResult = true;
+                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         if (mDeferTouchProcess) {
                             // still needs to set them for compute deltaX/Y
@@ -5618,7 +5603,7 @@
                     ted.mReprocess = mDeferTouchProcess;
                     ted.mNativeLayer = mScrollingLayer;
                     ted.mNativeLayerRect.set(mScrollingLayerRect);
-                    ted.mDontEnqueueResult = true;
+                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     mLastSentTouchTime = eventTime;
                     if (mDeferTouchProcess) {
@@ -5800,7 +5785,7 @@
                     ted.mReprocess = mDeferTouchProcess;
                     ted.mNativeLayer = mScrollingLayer;
                     ted.mNativeLayerRect.set(mScrollingLayerRect);
-                    ted.mDontEnqueueResult = true;
+                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                 }
                 mLastTouchUpTime = eventTime;
@@ -5823,7 +5808,7 @@
                             ted.mNativeLayer = nativeScrollableLayer(
                                     contentX, contentY,
                                     ted.mNativeLayerRect, null);
-                            ted.mDontEnqueueResult = true;
+                            ted.mSequence = mTouchEventQueue.nextTouchSequence();
                             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         } else if (mPreventDefault != PREVENT_DEFAULT_YES){
                             mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
@@ -6017,7 +6002,6 @@
             // set mLastTouchX/Y to the remaining point
             mLastTouchX = Math.round(x);
             mLastTouchY = Math.round(y);
-            mIsHandlingMultiTouch = false;
         } else if (action == MotionEvent.ACTION_MOVE) {
             // negative x or y indicate it is on the edge, skip it.
             if (x < 0 || y < 0) {
@@ -6041,7 +6025,7 @@
             ted.mAction = MotionEvent.ACTION_CANCEL;
             ted.mNativeLayer = nativeScrollableLayer(
                     x, y, ted.mNativeLayerRect, null);
-            ted.mDontEnqueueResult = true;
+            ted.mSequence = mTouchEventQueue.nextTouchSequence();
             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
             mPreventDefault = PREVENT_DEFAULT_IGNORE;
         }
@@ -7291,8 +7275,6 @@
         private void handleQueuedMotionEvent(MotionEvent ev) {
             int action = ev.getActionMasked();
             if (ev.getPointerCount() > 1) {  // Multi-touch
-                mIsHandlingMultiTouch = true;
-
                 handleMultiTouchInWebView(ev);
             } else {
                 final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
@@ -7331,7 +7313,6 @@
                 if (ted.mPoints.length > 1) {  // multi-touch
                     if (ted.mAction == MotionEvent.ACTION_POINTER_UP &&
                             ted.mMotionEvent.getPointerCount() == 2) {
-                        mIsHandlingMultiTouch = false;
                     }
                     if (!ted.mNativeResult) {
                         mPreventDefault = PREVENT_DEFAULT_NO;
@@ -7525,7 +7506,7 @@
                         ted.mNativeLayer = nativeScrollableLayer(
                                 ted.mPoints[0].x, ted.mPoints[0].y,
                                 ted.mNativeLayerRect, null);
-                        ted.mDontEnqueueResult = true;
+                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
                         mTouchMode = TOUCH_DONE_MODE;
@@ -7571,7 +7552,7 @@
                 case NEW_PICTURE_MSG_ID: {
                     // called for new content
                     final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
-                    setBaseLayer(draw.mBaseLayer, draw.mInvalRegion.getBounds(),
+                    setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
                             getSettings().getShowVisualIndicator());
                     final Point viewSize = draw.mViewSize;
                     WebViewCore.ViewState viewState = draw.mViewState;
@@ -7741,9 +7722,12 @@
                         break;
                     }
                     TouchEventData ted = (TouchEventData) msg.obj;
-                    if (!ted.mDontEnqueueResult) {
-                        mTouchEventQueue.enqueueTouchEvent(ted);
-                    }
+
+                    // WebCore is responding to us; remove pending timeout.
+                    // It will be re-posted when needed.
+                    removeMessages(PREVENT_DEFAULT_TIMEOUT);
+
+                    mTouchEventQueue.enqueueTouchEvent(ted);
                     break;
 
                 case REQUEST_KEYBOARD:
@@ -8613,7 +8597,7 @@
     private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
     private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native void     nativeSetBaseLayer(int layer, Rect invalRect,
+    private native void     nativeSetBaseLayer(int layer, Region invalRegion,
             boolean showVisualIndicator);
     private native void     nativeShowCursorTimed();
     private native void     nativeReplaceBaseContent(int content);
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index db605de..65026a5 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -256,4 +256,18 @@
      */
     public void onScaleChanged(WebView view, float oldScale, float newScale) {
     }
+
+    /**
+     * Notify the host application that a request to automatically log in the
+     * user has been processed.
+     * @param view The WebView requesting the login.
+     * @param realm The account realm used to look up accounts.
+     * @param account An optional account. If not null, the account should be
+     *                checked against accounts on the device. If it is a valid
+     *                account, it should be used to log in the user.
+     * @param args Authenticator specific arguments used to log in the user.
+     */
+    public void onReceivedLoginRequest(WebView view, String realm,
+            String account, String args) {
+    }
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index f367b93..b920a30 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -827,7 +827,6 @@
         Rect mNativeLayerRect = new Rect();
         long mSequence;
         boolean mNativeResult;
-        boolean mDontEnqueueResult;
     }
 
     static class GeolocationPermissionsData {
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 942425af..716a215 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1008,14 +1008,15 @@
         final Point viewSize = drawData.mViewSize;
         updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
         setupZoomOverviewWidth(drawData, mWebView.getViewWidth());
+        final float overviewScale = getZoomOverviewScale();
         if (!mMinZoomScaleFixed) {
-            mMinZoomScale = getZoomOverviewScale();
+            mMinZoomScale = (mInitialScale > 0) ?
+                    Math.min(mInitialScale, overviewScale) : overviewScale;
             mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale);
         }
 
         if (!mWebView.drawHistory()) {
             float scale;
-            final float overviewScale = getZoomOverviewScale();
             WebSettings settings = mWebView.getSettings();
 
             if (mInitialScale > 0) {
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d92588cb..17b3bda 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -477,8 +477,7 @@
                 break;
             case MotionEvent.ACTION_POINTER_DOWN: {
                 final int index = ev.getActionIndex();
-                final float x = ev.getX(index);
-                mLastMotionX = x;
+                mLastMotionX = ev.getX(index);
                 mActivePointerId = ev.getPointerId(index);
                 break;
             }
@@ -1446,6 +1445,7 @@
         super.setOverScrollMode(mode);
     }
 
+    @SuppressWarnings({"SuspiciousNameCombination"})
     @Override
     public void draw(Canvas canvas) {
         super.draw(canvas);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 2d164fd..6088654 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -83,6 +83,7 @@
     private CursorAdapter mSuggestionsAdapter;
     private View mSearchButton;
     private View mSubmitButton;
+    private View mSearchPlate;
     private View mSubmitArea;
     private ImageView mCloseButton;
     private View mSearchEditFrame;
@@ -190,6 +191,7 @@
         mQueryTextView.setSearchView(this);
 
         mSearchEditFrame = findViewById(R.id.search_edit_frame);
+        mSearchPlate = findViewById(R.id.search_plate);
         mSubmitArea = findViewById(R.id.submit_area);
         mSubmitButton = findViewById(R.id.search_go_btn);
         mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
@@ -258,6 +260,7 @@
         mSearchable = searchable;
         if (mSearchable != null) {
             updateSearchAutoComplete();
+            updateQueryHint();
         }
         // Cache the voice search capability
         mVoiceButtonEnabled = hasVoiceSearch();
@@ -575,19 +578,19 @@
     }
 
     private void updateSubmitButton(boolean hasText) {
-        mSubmitButton.setVisibility(
-                isSubmitAreaEnabled() ? (hasText ? VISIBLE : INVISIBLE) : GONE);
+        int visibility = GONE;
+        if (isSubmitAreaEnabled() && hasFocus() && (hasText || !mVoiceButtonEnabled)) {
+            visibility = VISIBLE;
+        }
+        mSubmitButton.setVisibility(visibility);
     }
 
     private void updateSubmitArea() {
         int visibility = GONE;
-        if (isSubmitAreaEnabled()) {
-            if (mSubmitButton.getVisibility() == VISIBLE
-                    || mVoiceButton.getVisibility() == VISIBLE) {
-                visibility = VISIBLE;
-            } else {
-                visibility = INVISIBLE;
-            }
+        if (isSubmitAreaEnabled()
+                && (mSubmitButton.getVisibility() == VISIBLE
+                        || mVoiceButton.getVisibility() == VISIBLE)) {
+            visibility = VISIBLE;
         }
         mSubmitArea.setVisibility(visibility);
     }
@@ -601,6 +604,11 @@
         mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
     }
 
+    private void updateFocusedState(boolean focused) {
+        mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+        mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+    }
+
     private void setImeVisibility(boolean visible) {
         InputMethodManager imm = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -851,16 +859,11 @@
      * Update the visibility of the voice button.  There are actually two voice search modes,
      * either of which will activate the button.
      * @param empty whether the search query text field is empty. If it is, then the other
-     * criteria apply to make the voice button visible. Otherwise the voice button will not
-     * be visible - i.e., if the user has typed a query, remove the voice button.
+     * criteria apply to make the voice button visible.
      */
     private void updateVoiceButton(boolean empty) {
-        // If the voice button is to be visible, show it
-        // Else, make it gone if the submit button is enabled, otherwise invisible to
-        // avoid losing the real-estate
-        int visibility = mSubmitButtonEnabled ? GONE : INVISIBLE;
-
-        if (mVoiceButtonEnabled && !isIconified() && empty) {
+        int visibility = GONE;
+        if (mVoiceButtonEnabled && !isIconified() && (empty || !mSubmitButtonEnabled)) {
             visibility = VISIBLE;
             mSubmitButton.setVisibility(GONE);
         }
@@ -958,7 +961,8 @@
     }
 
     void onTextFocusChanged() {
-        updateCloseButton();
+        updateViewsVisibility(isIconified());
+        updateFocusedState(mQueryTextView.hasFocus());
     }
 
     private boolean onItemClicked(int position, int actionKey, String actionMsg) {
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index bab469b..21c61bd 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -531,6 +531,8 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        boolean expandClipRegion = false;
+
         canvas.getClipBounds(stackInvalidateRect);
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -540,12 +542,22 @@
                     child.getAlpha() == 0f || child.getVisibility() != VISIBLE) {
                 lp.resetInvalidateRect();
             }
-            stackInvalidateRect.union(lp.getInvalidateRect());
+            Rect childInvalidateRect = lp.getInvalidateRect();
+            if (!childInvalidateRect.isEmpty()) {
+                expandClipRegion = true;
+                stackInvalidateRect.union(childInvalidateRect);
+            }
         }
-        canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(stackInvalidateRect, Region.Op.UNION);
-        super.dispatchDraw(canvas);
-        canvas.restore();
+
+        // We only expand the clip bounds if necessary.
+        if (expandClipRegion) {
+            canvas.save(Canvas.CLIP_SAVE_FLAG);
+            canvas.clipRect(stackInvalidateRect, Region.Op.UNION);
+            super.dispatchDraw(canvas);
+            canvas.restore();
+        } else {
+            super.dispatchDraw(canvas);
+        }
     }
 
     private void onLayout() {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index a41b348..586ba87 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -43,6 +43,7 @@
 import android.view.ViewParent;
 import android.view.Window;
 import android.widget.AdapterView;
+import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -957,4 +958,60 @@
             }
         }
     }
+
+    private static class HomeView extends FrameLayout {
+        private View mUpView;
+        private View mIconView;
+
+        public HomeView(Context context) {
+            this(context, null);
+        }
+
+        public HomeView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        protected void onFinishInflate() {
+            mUpView = findViewById(com.android.internal.R.id.up);
+            mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
+            final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
+            int width = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
+            int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
+            measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
+            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+            width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
+            height = Math.max(height,
+                    iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin);
+            setMeasuredDimension(width, height);
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+            final int vCenter = (b - t) / 2;
+            int width = r - l;
+            if (mUpView.getVisibility() != GONE) {
+                final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
+                final int upHeight = mUpView.getMeasuredHeight();
+                final int upWidth = mUpView.getMeasuredWidth();
+                final int upTop = t + vCenter - upHeight / 2;
+                mUpView.layout(l, upTop, l + upWidth, upTop + upHeight);
+                final int upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
+                width -= upOffset;
+                l += upOffset;
+            }
+            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+            final int iconHeight = mIconView.getMeasuredHeight();
+            final int iconWidth = mIconView.getMeasuredWidth();
+            final int hCenter = (r - l) / 2;
+            final int iconLeft = l + iconLp.leftMargin + hCenter - iconWidth / 2;
+            final int iconTop = t + iconLp.topMargin + vCenter - iconHeight / 2;
+            mIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight);
+        }
+    }
 }
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index f78f83c..b6619ab 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -315,7 +315,11 @@
     }
 
     // get the pointer to where we'll record the audio
-    recordBuff = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
+    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
+    // a way that it becomes much more efficient. When doing so, we will have to prevent the
+    // AudioSystem callback to be called while in critical section (in case of media server
+    // process crash for instance)
+    recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
 
     if (recordBuff == NULL) {
         LOGE("Error retrieving destination for recorded audio data, can't record");
@@ -327,7 +331,7 @@
     ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, 
                                         sizeInBytes > (jint)recorderBuffSize ? 
                                             (jint)recorderBuffSize : sizeInBytes );
-    env->ReleasePrimitiveArrayCritical(javaAudioData, recordBuff, 0);
+    env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
 
     return (jint) readSize;
 }
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 8409adc..44d2a52 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -530,8 +530,12 @@
     }
 
     // get the pointer for the audio data from the java array
+    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
+    // a way that it becomes much more efficient. When doing so, we will have to prevent the
+    // AudioSystem callback to be called while in critical section (in case of media server
+    // process crash for instance)
     if (javaAudioData) {
-        cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
+        cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
         if (cAudioData == NULL) {
             LOGE("Error retrieving source of audio data to play, can't play");
             return 0; // out of memory or no data to load
@@ -543,7 +547,7 @@
 
     jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes);
 
-    env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0);
+    env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0);
 
     //LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d",
     //     (int)written, (int)(sizeInBytes), (int)offsetInBytes);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0ad174f..c684e7e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1379,12 +1379,6 @@
                 android:excludeFromRecents="true">
         </activity>
 
-        <activity android:name="com.android.server.usb.UsbResolverActivity"
-            android:theme="@style/Theme.Holo.Dialog.Alert"
-            android:finishOnCloseSystemDialogs="true"
-            android:excludeFromRecents="true">
-        </activity>
-
         <service android:name="com.android.server.LoadAverageService"
                 android:exported="true" />
 
diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png
index b9435b6..ae77fa0 100644
--- a/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png
index 477d820..c6bdfcc 100644
--- a/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
index b9435b6..ae77fa0 100644
--- a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
index 477d820..c6bdfcc 100644
--- a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
Binary files differ
diff --git a/core/res/res/layout/action_bar_home.xml b/core/res/res/layout/action_bar_home.xml
index 7867577..c82f91d 100644
--- a/core/res/res/layout/action_bar_home.xml
+++ b/core/res/res/layout/action_bar_home.xml
@@ -14,14 +14,14 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="wrap_content"
-              android:layout_height="match_parent"
-              android:background="?android:attr/selectableItemBackground"
-              android:orientation="horizontal">
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+      class="com.android.internal.widget.ActionBarView$HomeView"
+      android:layout_width="wrap_content"
+      android:layout_height="match_parent"
+      android:background="?android:attr/selectableItemBackground" >
     <ImageView android:id="@android:id/up"
                android:src="?android:attr/homeAsUpIndicator"
-               android:layout_gravity="top|left"
+               android:layout_gravity="center_vertical|left"
                android:visibility="gone"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
@@ -33,4 +33,4 @@
                android:paddingRight="16dip"
                android:layout_gravity="center"
                android:scaleType="center" />
-</LinearLayout>
+</view>
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
index 93b6deb..c52b73f 100644
--- a/core/res/res/layout/search_view.xml
+++ b/core/res/res/layout/search_view.xml
@@ -54,6 +54,10 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:layout_gravity="center_vertical"
+        android:layout_marginLeft="4dip"
+        android:layout_marginRight="4dip"
+        android:layout_marginTop="4dip"
+        android:layout_marginBottom="4dip"
         android:orientation="horizontal">
 
         <!-- Inner layout contains the app icon, button(s) and EditText -->
diff --git a/docs/html/images/avd-manager.png b/docs/html/images/avd-manager.png
index 69ce972..c33d8a8 100644
--- a/docs/html/images/avd-manager.png
+++ b/docs/html/images/avd-manager.png
Binary files differ
diff --git a/docs/html/images/billing_package.png b/docs/html/images/billing_package.png
old mode 100755
new mode 100644
index ec04c2d..951e117
--- a/docs/html/images/billing_package.png
+++ b/docs/html/images/billing_package.png
Binary files differ
diff --git a/docs/html/images/developing/adt-props-isLib.png b/docs/html/images/developing/adt-props-isLib.png
index 18bdb33..49c9111 100644
--- a/docs/html/images/developing/adt-props-isLib.png
+++ b/docs/html/images/developing/adt-props-isLib.png
Binary files differ
diff --git a/docs/html/images/developing/adt-props-libRef.png b/docs/html/images/developing/adt-props-libRef.png
index e61df51..73bccbd 100644
--- a/docs/html/images/developing/adt-props-libRef.png
+++ b/docs/html/images/developing/adt-props-libRef.png
Binary files differ
diff --git a/docs/html/images/licensing_add_library.png b/docs/html/images/licensing_add_library.png
index 90b4435..3bbe6d5 100644
--- a/docs/html/images/licensing_add_library.png
+++ b/docs/html/images/licensing_add_library.png
Binary files differ
diff --git a/docs/html/images/licensing_gapis_8.png b/docs/html/images/licensing_gapis_8.png
index 43ad262..480d989 100644
--- a/docs/html/images/licensing_gapis_8.png
+++ b/docs/html/images/licensing_gapis_8.png
Binary files differ
diff --git a/docs/html/images/licensing_package.png b/docs/html/images/licensing_package.png
index 5da5632..eb2c5cf 100644
--- a/docs/html/images/licensing_package.png
+++ b/docs/html/images/licensing_package.png
Binary files differ
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 3a7b39f..97717fe 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -594,13 +594,14 @@
 
 <p>ADT is a plugin for the Eclipse IDE. Before you can install or use ADT,
 you must have a compatible version of Eclipse installed on your development
-computer. </p>
+computer. Check the <a
+href="requirements.html">System Requirements</a> document for
+a list of Eclipse versions that are compatible with the Android SDK.</li></p>
 
 <ul>
 <li>If Eclipse is already installed on your computer, make sure that it is
-a version that is compatible with ADT and the Android SDK. Check the <a
-href="requirements.html">System Requirements</a> document for
-a list of Eclipse versions that are compatible with the Android SDK.</li>
+a version that is compatible with ADT and the Android SDK. 
+
 <li>If you need to install or update Eclipse, you can download it from this
 location:
 
@@ -608,7 +609,7 @@
 "http://www.eclipse.org/downloads/">http://www.eclipse.org/downloads/</a>
 </p>
 
-<p>For Eclipse 3.5 or newer, the "Eclipse Classic" version is recommended. Otherwise, a Java or RCP
+<p>The "Eclipse Classic" version is recommended. Otherwise, a Java or RCP
 version of Eclipse is recommended.</p></li>
 </ul>
 
@@ -624,19 +625,15 @@
 
 <h3 id="downloading">Downloading the ADT Plugin</h3>
 
-<p>Use Update Manager feature of your Eclipse installation to install the latest
+<p>Use the Update Manager feature of your Eclipse installation to install the latest
 revision of ADT on your development computer.<p>
 
 <p>Assuming that you have a compatible version of the Eclipse IDE installed, as
 described in <a href="#preparing">Preparing for Installation</a>, above, follow
 these steps to download the ADT plugin and install it in your Eclipse
-environment. </p>
+environment.</p>
 
-<table style="font-size:100%">
-<tr><th>Eclipse 3.5 (Galileo) and 3.6 (Helios)</th><th>Eclipse 3.4 (Ganymede)</th></tr>
-<tr>
-<td width="45%">
-<!-- 3.5+ steps -->
+
 <ol>
     <li>Start Eclipse, then select <strong>Help</strong> &gt; <strong>Install New
 Software...</strong>.</li>
@@ -655,35 +652,6 @@
     <li>When the installation completes, restart Eclipse. </li>
 </ol>
 
-</td>
-<td width="50%">
-
-<!-- 3.4 steps -->
-<ol>
-    <li>Start Eclipse, then select <strong>Help</strong> &gt; <strong>Software Updates...</strong>.
-In the dialog that appears, click the <strong>Available Software</strong> tab.</li>
-    <li>Click <strong>Add Site</strong>.</li>
-    <li>In the Add Site dialog that appears, enter this URL in the "Location" field:
-        <pre>https://dl-ssl.google.com/android/eclipse/</pre>
-        <p>Note: If you have trouble acquiring the plugin, you can try
-           using "http" in the URL, instead of "https" (https is preferred for
-           security reasons).</p>
-        <p>Click <strong>OK</strong>.</p>
-    </li>
-    <li>Back in the Available Software view, you should see the plugin listed by the URL,
-       with "Developer Tools" nested within it. Select the checkbox next to Developer Tools,
-        which will automatically select the nested tools. Then click
-        <strong>Install</strong></li>
-    <li>On the subsequent Install window, all of the included tools
-        should be checked. Click <strong>Next</strong>. </li>
-    <li>Read and accept the license agreements, then click <strong>Finish</strong>.</li>
-    <li>When the installation completes, restart Eclipse. </li>
-
-</ol>
-</td>
-</tr>
-</table>
-
 <h3 id="configuring">Configuring the ADT Plugin</h3>
 
 <p>Once you've successfully downloaded ADT as described above, the next step
@@ -807,11 +775,6 @@
 <p>Follow the steps below to check whether an update is available and, if so,
 to install it. </p>
 
-<table style="font-size:100%">
-<tr><th>Eclipse 3.5 (Galileo) and 3.6 (Helios)</th><th>Eclipse 3.4 (Ganymede)</th></tr>
-<tr>
-<td>
-<!-- 3.5+ steps -->
 <ol>
     <li>Select <strong>Help</strong> &gt; <strong>Check for Updates</strong>.
       <p>If there are no updates available, a dialog will say so and you're done.</p></li>
@@ -823,25 +786,6 @@
       Android Development Tools.</li>
     <li>Restart Eclipse.</li>
 </ol>
-</td>
-
-<td width="50%">
-<!-- 3.4 steps -->
-<ol>
-    <li>Select <strong>Help</strong> &gt; <strong>Software Updates</strong>.</li>
-    <li>Select the <strong>Available Software</strong> tab.</li>
-    <li>If there are updates available, select Android DDMS, Android Development Tools,
-      and Android Hierarchy Viewer, then click <strong>Update</strong>.</li>
-    <li>In the resulting Available Updates dialog, ensure that each of the listed tools
-      are selected, then click <strong>Next</strong>.</li>
-    <li>Read and accept the license agreement and then click <strong>Finish</strong>.
-      This will download and install the latest version of Android DDMS and
-      Android Development Tools.</li>
-    <li>Restart Eclipse.</li>
-</ol>
-</td>
-</tr>
-</table>
 
 
 <p>If you encounter problems during the update, remove the existing ADT plugin from Eclipse, then
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index b0fd761..a1080c2 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -96,13 +96,14 @@
 <p>If you will be developing in Eclipse with the Android Development
 Tools (ADT) Plugin&mdash;the recommended path if you are new to
 Android&mdash;make sure that you have a suitable version of Eclipse
-installed on your computer (3.4 or newer is recommended). If you need
-to install Eclipse, you can download it from this location: </p>
+installed on your computer as described in the
+<a href="requirements.html">System Requirements</a> document.
+If you need to install Eclipse, you can download it from this location: </p>
 
 <p style="margin-left:2em;"><a href=
 "http://www.eclipse.org/downloads/">http://www.eclipse.org/downloads/</a></p>
 
-<p>For Eclipse 3.5 or newer, the "Eclipse Classic" version is recommended. Otherwise, a Java or
+<p>The "Eclipse Classic" version is recommended. Otherwise, a Java or
 RCP version of Eclipse is recommended.</p>
 
 
diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd
index 3d62dd9..f12d0aa 100644
--- a/docs/html/sdk/requirements.jd
+++ b/docs/html/sdk/requirements.jd
@@ -24,7 +24,9 @@
 
 <h4 style="margin-top:.25em"><em>Eclipse IDE</em></h4>
     <ul>
-      <li>Eclipse 3.4 (Ganymede) or greater</li>
+      <li>Eclipse 3.5 (Galileo) or greater 
+<p class="note"><strong>Note:</strong> Eclipse 3.4 (Ganymede) is no longer
+supported with the latest version of ADT.</p></li>
       <li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included
 in most Eclipse IDE packages) </li>
       <li>If you need to install or update Eclipse, you can download it from <a
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 5f7cd90..293764d 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -346,12 +346,14 @@
     };
 
             bool processAudioBuffer(const sp<ClientRecordThread>& thread);
-            status_t openRecord(uint32_t sampleRate,
+            status_t openRecord_l(uint32_t sampleRate,
                                 int format,
                                 int channelCount,
                                 int frameCount,
                                 uint32_t flags,
                                 audio_io_handle_t input);
+            audio_io_handle_t getInput_l();
+            status_t restoreRecord_l(audio_track_cblk_t*& cblk);
 
     sp<IAudioRecord>        mAudioRecord;
     sp<IMemory>             mCblkMemory;
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 813a905..3e346db 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -437,7 +437,7 @@
     };
 
             bool processAudioBuffer(const sp<AudioTrackThread>& thread);
-            status_t createTrack(int streamType,
+            status_t createTrack_l(int streamType,
                                  uint32_t sampleRate,
                                  int format,
                                  int channelCount,
@@ -446,6 +446,10 @@
                                  const sp<IMemory>& sharedBuffer,
                                  audio_io_handle_t output,
                                  bool enforceFrameCount);
+            void flush_l();
+            status_t setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
+            audio_io_handle_t getOutput_l();
+            status_t restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart);
 
     sp<IAudioTrack>         mAudioTrack;
     sp<IMemory>             mCblkMemory;
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index c6990bf..4610135 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -31,6 +31,7 @@
 #define MAX_STARTUP_TIMEOUT_MS  3000    // Longer timeout period at startup to cope with A2DP init time
 #define MAX_RUN_TIMEOUT_MS      1000
 #define WAIT_PERIOD_MS          10
+#define RESTORE_TIMEOUT_MS      5000    // Maximum waiting time for a track to be restored
 
 #define CBLK_UNDERRUN_MSK       0x0001
 #define CBLK_UNDERRUN_ON        0x0001  // underrun (out) or overrrun (in) indication
@@ -47,6 +48,12 @@
 #define CBLK_DISABLED_MSK       0x0010
 #define CBLK_DISABLED_ON        0x0010  // track disabled by AudioFlinger due to underrun:
 #define CBLK_DISABLED_OFF       0x0000  // must be re-started
+#define CBLK_RESTORING_MSK      0x0020
+#define CBLK_RESTORING_ON       0x0020  // track is being restored after invalidation
+#define CBLK_RESTORING_OFF      0x0000  // by AudioFlinger
+#define CBLK_RESTORED_MSK       0x0040
+#define CBLK_RESTORED_ON        0x0040  // track has been restored after invalidation
+#define CBLK_RESTORED_OFF       0x0040  // by AudioFlinger
 
 struct audio_track_cblk_t
 {
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 4a5620d..f9e29f1 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -592,11 +592,11 @@
 void Context::launchThreads(WorkerCallback_t cbk, void *data) {
     mWorkers.mLaunchData = data;
     mWorkers.mLaunchCallback = cbk;
-    mWorkers.mRunningCount = (int)mWorkers.mCount;
+    android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount);
     for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
         mWorkers.mLaunchSignals[ct].set();
     }
-    while (mWorkers.mRunningCount) {
+    while (android_atomic_acquire_load(&mWorkers.mRunningCount) != 0) {
         mWorkers.mCompleteSignal.wait();
     }
 }
@@ -707,8 +707,8 @@
     }
 
     mWorkers.mCompleteSignal.init();
-    mWorkers.mRunningCount = 0;
-    mWorkers.mLaunchCount = 0;
+    android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount);
+    android_atomic_release_store(0, &mWorkers.mLaunchCount);
     for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
         status = pthread_create(&mWorkers.mThreadId[ct], &threadAttr, helperThreadProc, this);
         if (status) {
@@ -717,6 +717,9 @@
             break;
         }
     }
+    while (android_atomic_acquire_load(&mWorkers.mRunningCount) != 0) {
+        usleep(100);
+    }
     pthread_attr_destroy(&threadAttr);
     return true;
 }
@@ -736,14 +739,14 @@
     // Cleanup compute threads.
     mWorkers.mLaunchData = NULL;
     mWorkers.mLaunchCallback = NULL;
-    mWorkers.mRunningCount = (int)mWorkers.mCount;
+    android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount);
     for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
         mWorkers.mLaunchSignals[ct].set();
     }
     for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
         status = pthread_join(mWorkers.mThreadId[ct], &res);
     }
-    rsAssert(!mWorkers.mRunningCount);
+    rsAssert(android_atomic_acquire_load(&mWorkers.mRunningCount) == 0);
 
     // Global structure cleanup.
     pthread_mutex_lock(&gInitMutex);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 5a73d2d..fd12e19 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -805,7 +805,7 @@
             if (mode != mMode) {
 
                 // automatically handle audio focus for mode changes
-                handleFocusForCalls(mMode, mode);
+                handleFocusForCalls(mMode, mode, cb);
 
                 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
                     mMode = mode;
@@ -864,7 +864,7 @@
     }
 
     /** pre-condition: oldMode != newMode */
-    private void handleFocusForCalls(int oldMode, int newMode) {
+    private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) {
         // if ringing
         if (newMode == AudioSystem.MODE_RINGTONE) {
             // if not ringing silently
@@ -872,8 +872,8 @@
             if (ringVolume > 0) {
                 // request audio focus for the communication focus entry
                 requestAudioFocus(AudioManager.STREAM_RING,
-                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
-                        null, null /* both allowed to be null only for this clientId */,
+                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
+                        null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
                         IN_VOICE_COMM_FOCUS_ID /*clientId*/);
 
             }
@@ -884,8 +884,8 @@
             // request audio focus for the communication focus entry
             // (it's ok if focus was already requested during ringing)
             requestAudioFocus(AudioManager.STREAM_RING,
-                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
-                    null, null /* both allowed to be null only for this clientId */,
+                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
+                    null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
                     IN_VOICE_COMM_FOCUS_ID /*clientId*/);
         }
         // if exiting call
@@ -2547,10 +2547,9 @@
         // the main stream type for the audio focus request is currently not used. It may
         // potentially be used to handle multiple stream type-dependent audio focuses.
 
-        // we need a valid binder callback for clients other than the AudioService's phone
-        // state listener
-        if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) {
-            Log.i(TAG, " AudioFocus  DOA client for requestAudioFocus(), exiting");
+        // we need a valid binder callback for clients
+        if (!cb.pingBinder()) {
+            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
 
@@ -2591,17 +2590,14 @@
         }//synchronized(mAudioFocusLock)
 
         // handle the potential premature death of the new holder of the focus
-        // (premature death == death before abandoning focus) for a client which is not the
-        // AudioService's phone state listener
-        if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
-            // Register for client death notification
-            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
-            try {
-                cb.linkToDeath(afdh, 0);
-            } catch (RemoteException e) {
-                // client has already died!
-                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
-            }
+        // (premature death == death before abandoning focus)
+        // Register for client death notification
+        AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
+        try {
+            cb.linkToDeath(afdh, 0);
+        } catch (RemoteException e) {
+            // client has already died!
+            Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
         }
 
         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java
index 40e2f9b..d25dcb9 100644
--- a/media/java/android/mtp/MtpClient.java
+++ b/media/java/android/mtp/MtpClient.java
@@ -16,6 +16,7 @@
 
 package android.mtp;
 
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -41,6 +42,9 @@
 
     private static final String TAG = "MtpClient";
 
+    private static final String ACTION_USB_PERMISSION =
+            "android.mtp.MtpClient.action.USB_PERMISSION";
+
     private final Context mContext;
     private final UsbManager mUsbManager;
     private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
@@ -49,29 +53,47 @@
     // mDevices is also used for synchronization in this class.
     private final HashMap<String, MtpDevice> mDevices = new HashMap<String, MtpDevice>();
 
+    private final PendingIntent mPermissionIntent;
+
     private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
             UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
             String deviceName = usbDevice.getDeviceName();
 
             synchronized (mDevices) {
                 MtpDevice mtpDevice = mDevices.get(deviceName);
 
-                if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+                if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                     if (mtpDevice == null) {
                         mtpDevice = openDeviceLocked(usbDevice);
                     }
                     if (mtpDevice != null) {
-                        mDevices.put(deviceName, mtpDevice);
                         for (Listener listener : mListeners) {
                             listener.deviceAdded(mtpDevice);
                         }
                     }
-                } else if (mtpDevice != null) {
-                    mDevices.remove(deviceName);
-                    for (Listener listener : mListeners) {
-                        listener.deviceRemoved(mtpDevice);
+                } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+                    if (mtpDevice != null) {
+                        mDevices.remove(deviceName);
+                        for (Listener listener : mListeners) {
+                            listener.deviceRemoved(mtpDevice);
+                        }
+                    }
+                } else if (ACTION_USB_PERMISSION.equals(action)) {
+                    boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED,
+                            false);
+                    Log.d(TAG, "ACTION_USB_PERMISSION: " + permission);
+                    if (permission) {
+                        if (mtpDevice == null) {
+                            mtpDevice = openDeviceLocked(usbDevice);
+                        }
+                        if (mtpDevice != null) {
+                            for (Listener listener : mListeners) {
+                                listener.deviceAdded(mtpDevice);
+                            }
+                        }
                     }
                 }
             }
@@ -126,10 +148,11 @@
     public MtpClient(Context context) {
         mContext = context;
         mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
-
+        mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
         IntentFilter filter = new IntentFilter();
         filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        filter.addAction(ACTION_USB_PERMISSION);
         context.registerReceiver(mUsbReceiver, filter);
     }
 
@@ -142,9 +165,14 @@
      */
     private MtpDevice openDeviceLocked(UsbDevice usbDevice) {
         if (isCamera(usbDevice)) {
-            MtpDevice mtpDevice = new MtpDevice(usbDevice);
-            if (mtpDevice.open(mUsbManager)) {
-                return mtpDevice;
+            if (!mUsbManager.hasPermission(usbDevice)) {
+                mUsbManager.requestPermission(usbDevice, mPermissionIntent);
+            } else {
+                MtpDevice mtpDevice = new MtpDevice(usbDevice);
+                if (mtpDevice.open(mUsbManager)) {
+                    mDevices.put(usbDevice.getDeviceName(), mtpDevice);
+                    return mtpDevice;
+                }
             }
         }
         return null;
@@ -218,13 +246,8 @@
             // Query the USB manager since devices might have attached
             // before we added our listener.
             for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
-                String deviceName = usbDevice.getDeviceName();
-                MtpDevice mtpDevice = mDevices.get(deviceName);
-                if (mtpDevice == null) {
-                   mtpDevice = openDeviceLocked(usbDevice);
-                }
-                if (mtpDevice != null) {
-                    mDevices.put(deviceName, mtpDevice);
+                if (mDevices.get(usbDevice.getDeviceName()) == null) {
+                    openDeviceLocked(usbDevice);
                 }
             }
 
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 1d6ffa0..a18bedb 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -128,6 +128,9 @@
 {
 
     LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
+
+    AutoMutex lock(mLock);
+
     if (mAudioRecord != 0) {
         return INVALID_OPERATION;
     }
@@ -183,7 +186,7 @@
     mSessionId = sessionId;
 
     // create the IAudioRecord
-    status = openRecord(sampleRate, format, channelCount,
+    status = openRecord_l(sampleRate, format, channelCount,
                         frameCount, flags, input);
     if (status != NO_ERROR) {
         return status;
@@ -282,21 +285,31 @@
      }
 
     AutoMutex lock(mLock);
+    // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+    // while we are accessing the cblk
+    sp <IAudioRecord> audioRecord = mAudioRecord;
+    sp <IMemory> iMem = mCblkMemory;
+    audio_track_cblk_t* cblk = mCblk;
     if (mActive == 0) {
         mActive = 1;
-        ret = mAudioRecord->start();
-        if (ret == DEAD_OBJECT) {
-            LOGV("start() dead IAudioRecord: creating a new one");
-            ret = openRecord(mCblk->sampleRate, mFormat, mChannelCount,
-                    mFrameCount, mFlags, getInput());
-            if (ret == NO_ERROR) {
-                ret = mAudioRecord->start();
+
+        cblk->lock.lock();
+        if (!(cblk->flags & CBLK_INVALID_MSK)) {
+            cblk->lock.unlock();
+            ret = mAudioRecord->start();
+            cblk->lock.lock();
+            if (ret == DEAD_OBJECT) {
+                cblk->flags |= CBLK_INVALID_MSK;
             }
         }
+        if (cblk->flags & CBLK_INVALID_MSK) {
+            ret = restoreRecord_l(cblk);
+        }
+        cblk->lock.unlock();
         if (ret == NO_ERROR) {
-            mNewPosition = mCblk->user + mUpdatePeriod;
-            mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
-            mCblk->waitTimeMs = 0;
+            mNewPosition = cblk->user + mUpdatePeriod;
+            cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+            cblk->waitTimeMs = 0;
             if (t != 0) {
                t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
             } else {
@@ -353,6 +366,7 @@
 
 uint32_t AudioRecord::getSampleRate()
 {
+    AutoMutex lock(mLock);
     return mCblk->sampleRate;
 }
 
@@ -400,6 +414,7 @@
 {
     if (position == 0) return BAD_VALUE;
 
+    AutoMutex lock(mLock);
     *position = mCblk->user;
 
     return NO_ERROR;
@@ -415,7 +430,8 @@
 
 // -------------------------------------------------------------------------
 
-status_t AudioRecord::openRecord(
+// must be called with mLock held
+status_t AudioRecord::openRecord_l(
         uint32_t sampleRate,
         int format,
         int channelCount,
@@ -459,6 +475,7 @@
 
 status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
 {
+    AutoMutex lock(mLock);
     int active;
     status_t result;
     audio_track_cblk_t* cblk = mCblk;
@@ -483,7 +500,19 @@
                 cblk->lock.unlock();
                 return WOULD_BLOCK;
             }
-            result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+            if (!(cblk->flags & CBLK_INVALID_MSK)) {
+                mLock.unlock();
+                result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+                cblk->lock.unlock();
+                mLock.lock();
+                if (mActive == 0) {
+                    return status_t(STOPPED);
+                }
+                cblk->lock.lock();
+            }
+            if (cblk->flags & CBLK_INVALID_MSK) {
+                goto create_new_record;
+            }
             if (__builtin_expect(result!=NO_ERROR, false)) {
                 cblk->waitTimeMs += waitTimeMs;
                 if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
@@ -491,16 +520,17 @@
                             "user=%08x, server=%08x", cblk->user, cblk->server);
                     cblk->lock.unlock();
                     result = mAudioRecord->start();
-                    if (result == DEAD_OBJECT) {
-                        LOGW("obtainBuffer() dead IAudioRecord: creating a new one");
-                        result = openRecord(cblk->sampleRate, mFormat, mChannelCount,
-                                            mFrameCount, mFlags, getInput());
-                        if (result == NO_ERROR) {
-                            cblk = mCblk;
-                            mAudioRecord->start();
-                        }
-                    }
                     cblk->lock.lock();
+                    if (result == DEAD_OBJECT) {
+                        cblk->flags |= CBLK_INVALID_MSK;
+create_new_record:
+                        result = AudioRecord::restoreRecord_l(cblk);
+                    }
+                    if (result != NO_ERROR) {
+                        LOGW("obtainBuffer create Track error %d", result);
+                        cblk->lock.unlock();
+                        return result;
+                    }
                     cblk->waitTimeMs = 0;
                 }
                 if (--waitCount == 0) {
@@ -540,12 +570,19 @@
 
 void AudioRecord::releaseBuffer(Buffer* audioBuffer)
 {
-    audio_track_cblk_t* cblk = mCblk;
-    cblk->stepUser(audioBuffer->frameCount);
+    AutoMutex lock(mLock);
+    mCblk->stepUser(audioBuffer->frameCount);
 }
 
 audio_io_handle_t AudioRecord::getInput()
 {
+    AutoMutex lock(mLock);
+    return getInput_l();
+}
+
+// must be called with mLock held
+audio_io_handle_t AudioRecord::getInput_l()
+{
     mInput = AudioSystem::getInput(mInputSource,
                                 mCblk->sampleRate,
                                 mFormat, mChannels,
@@ -573,6 +610,12 @@
         return BAD_VALUE;
     }
 
+    mLock.lock();
+    // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+    // while we are accessing the cblk
+    sp <IAudioRecord> audioRecord = mAudioRecord;
+    sp <IMemory> iMem = mCblkMemory;
+    mLock.unlock();
 
     do {
 
@@ -613,9 +656,17 @@
     uint32_t frames = mRemainingFrames;
     size_t readSize;
 
+    mLock.lock();
+    // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+    // while we are accessing the cblk
+    sp <IAudioRecord> audioRecord = mAudioRecord;
+    sp <IMemory> iMem = mCblkMemory;
+    audio_track_cblk_t* cblk = mCblk;
+    mLock.unlock();
+
     // Manage marker callback
     if (!mMarkerReached && (mMarkerPosition > 0)) {
-        if (mCblk->user >= mMarkerPosition) {
+        if (cblk->user >= mMarkerPosition) {
             mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
             mMarkerReached = true;
         }
@@ -623,7 +674,7 @@
 
     // Manage new position callback
     if (mUpdatePeriod > 0) {
-        while (mCblk->user >= mNewPosition) {
+        while (cblk->user >= mNewPosition) {
             mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
             mNewPosition += mUpdatePeriod;
         }
@@ -669,11 +720,11 @@
 
 
     // Manage overrun callback
-    if (mActive && (mCblk->framesAvailable_l() == 0)) {
-        LOGV("Overrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags);
-        if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
+    if (mActive && (cblk->framesAvailable() == 0)) {
+        LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
+        if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
             mCbf(EVENT_OVERRUN, mUserData, 0);
-            mCblk->flags |= CBLK_UNDERRUN_ON;
+            cblk->flags |= CBLK_UNDERRUN_ON;
         }
     }
 
@@ -685,6 +736,69 @@
     return true;
 }
 
+// must be called with mLock and cblk.lock held. Callers must also hold strong references on
+// the IAudioRecord and IMemory in case they are recreated here.
+// If the IAudioRecord is successfully restored, the cblk pointer is updated
+status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk)
+{
+    status_t result;
+
+    if (!(cblk->flags & CBLK_RESTORING_MSK)) {
+        LOGW("dead IAudioRecord, creating a new one");
+
+        cblk->flags |= CBLK_RESTORING_ON;
+        // signal old cblk condition so that other threads waiting for available buffers stop
+        // waiting now
+        cblk->cv.broadcast();
+        cblk->lock.unlock();
+
+        // if the new IAudioRecord is created, openRecord_l() will modify the
+        // following member variables: mAudioRecord, mCblkMemory and mCblk.
+        // It will also delete the strong references on previous IAudioRecord and IMemory
+        result = openRecord_l(cblk->sampleRate, mFormat, mChannelCount,
+                mFrameCount, mFlags, getInput_l());
+        if (result == NO_ERROR) {
+            result = mAudioRecord->start();
+        }
+        if (result != NO_ERROR) {
+            mActive = false;
+        }
+
+        // signal old cblk condition for other threads waiting for restore completion
+        cblk->lock.lock();
+        cblk->flags |= CBLK_RESTORED_MSK;
+        cblk->cv.broadcast();
+        cblk->lock.unlock();
+    } else {
+        if (!(cblk->flags & CBLK_RESTORED_MSK)) {
+            LOGW("dead IAudioRecord, waiting for a new one to be created");
+            mLock.unlock();
+            result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS));
+            cblk->lock.unlock();
+            mLock.lock();
+        } else {
+            LOGW("dead IAudioRecord, already restored");
+            result = NO_ERROR;
+            cblk->lock.unlock();
+        }
+        if (result != NO_ERROR || mActive == 0) {
+            result = status_t(STOPPED);
+        }
+    }
+    LOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
+         result, mActive, mCblk, cblk, mCblk->flags, cblk->flags);
+
+    if (result == NO_ERROR) {
+        // from now on we switch to the newly created cblk
+        cblk = mCblk;
+    }
+    cblk->lock.lock();
+
+    LOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result);
+
+    return result;
+}
+
 // =========================================================================
 
 AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava)
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index c1bed59..8d8f67b 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -148,6 +148,7 @@
 
     LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
 
+    AutoMutex lock(mLock);
     if (mAudioTrack != 0) {
         LOGE("Track already in use");
         return INVALID_OPERATION;
@@ -211,8 +212,15 @@
     mAuxEffectId = 0;
 
     // create the IAudioTrack
-    status_t status = createTrack(streamType, sampleRate, format, channelCount,
-                                  frameCount, flags, sharedBuffer, output, true);
+    status_t status = createTrack_l(streamType,
+                                  sampleRate,
+                                  format,
+                                  channelCount,
+                                  frameCount,
+                                  flags,
+                                  sharedBuffer,
+                                  output,
+                                  true);
 
     if (status != NO_ERROR) {
         return status;
@@ -312,37 +320,38 @@
      }
 
     AutoMutex lock(mLock);
+    // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
+    // while we are accessing the cblk
+    sp <IAudioTrack> audioTrack = mAudioTrack;
+    sp <IMemory> iMem = mCblkMemory;
+    audio_track_cblk_t* cblk = mCblk;
+
     if (mActive == 0) {
         mActive = 1;
-        mNewPosition = mCblk->server + mUpdatePeriod;
-        mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
-        mCblk->waitTimeMs = 0;
-        mCblk->flags &= ~CBLK_DISABLED_ON;
+        mNewPosition = cblk->server + mUpdatePeriod;
+        cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
+        cblk->waitTimeMs = 0;
+        cblk->flags &= ~CBLK_DISABLED_ON;
         if (t != 0) {
            t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
         } else {
             setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
         }
 
-        if (mCblk->flags & CBLK_INVALID_MSK) {
-            LOGW("start() track %p invalidated, creating a new one", this);
-            // no need to clear the invalid flag as this cblk will not be used anymore
-            // force new track creation
-            status = DEAD_OBJECT;
-        } else {
+        LOGV("start %p before lock cblk %p", this, mCblk);
+        cblk->lock.lock();
+        if (!(cblk->flags & CBLK_INVALID_MSK)) {
+            cblk->lock.unlock();
             status = mAudioTrack->start();
-        }
-        if (status == DEAD_OBJECT) {
-            LOGV("start() dead IAudioTrack: creating a new one");
-            status = createTrack(mStreamType, mCblk->sampleRate, mFormat, mChannelCount,
-                                 mFrameCount, mFlags, mSharedBuffer, getOutput(), false);
-            if (status == NO_ERROR) {
-                status = mAudioTrack->start();
-                if (status == NO_ERROR) {
-                    mNewPosition = mCblk->server + mUpdatePeriod;
-                }
+            cblk->lock.lock();
+            if (status == DEAD_OBJECT) {
+                cblk->flags |= CBLK_INVALID_MSK;
             }
         }
+        if (cblk->flags & CBLK_INVALID_MSK) {
+            status = restoreTrack_l(cblk, true);
+        }
+        cblk->lock.unlock();
         if (status != NO_ERROR) {
             LOGV("start() failed");
             mActive = 0;
@@ -375,14 +384,14 @@
         mAudioTrack->stop();
         // Cancel loops (If we are in the middle of a loop, playback
         // would not stop until loopCount reaches 0).
-        setLoop(0, 0, 0);
+        setLoop_l(0, 0, 0);
         // the playback head position will reset to 0, so if a marker is set, we need
         // to activate it again
         mMarkerReached = false;
         // Force flush if a shared buffer is used otherwise audioflinger
         // will not stop before end of buffer is reached.
         if (mSharedBuffer != 0) {
-            flush();
+            flush_l();
         }
         if (t != 0) {
             t->requestExit();
@@ -403,6 +412,13 @@
 
 void AudioTrack::flush()
 {
+    AutoMutex lock(mLock);
+    flush_l();
+}
+
+// must be called with mLock held
+void AudioTrack::flush_l()
+{
     LOGV("flush");
 
     // clear playback marker and periodic update counter
@@ -445,6 +461,7 @@
         return BAD_VALUE;
     }
 
+    AutoMutex lock(mLock);
     mVolume[LEFT] = left;
     mVolume[RIGHT] = right;
 
@@ -470,6 +487,7 @@
     if (level > 1.0f) {
         return BAD_VALUE;
     }
+    AutoMutex lock(mLock);
 
     mSendLevel = level;
 
@@ -495,17 +513,26 @@
     // Resampler implementation limits input sampling rate to 2 x output sampling rate.
     if (rate <= 0 || rate > afSamplingRate*2 ) return BAD_VALUE;
 
+    AutoMutex lock(mLock);
     mCblk->sampleRate = rate;
     return NO_ERROR;
 }
 
 uint32_t AudioTrack::getSampleRate()
 {
+    AutoMutex lock(mLock);
     return mCblk->sampleRate;
 }
 
 status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
 {
+    AutoMutex lock(mLock);
+    return setLoop_l(loopStart, loopEnd, loopCount);
+}
+
+// must be called with mLock held
+status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
+{
     audio_track_cblk_t* cblk = mCblk;
 
     Mutex::Autolock _l(cblk->lock);
@@ -540,6 +567,7 @@
 
 status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount)
 {
+    AutoMutex lock(mLock);
     if (loopStart != 0) {
         *loopStart = mCblk->loopStart;
     }
@@ -599,6 +627,7 @@
 
 status_t AudioTrack::setPosition(uint32_t position)
 {
+    AutoMutex lock(mLock);
     Mutex::Autolock _l(mCblk->lock);
 
     if (!stopped()) return INVALID_OPERATION;
@@ -614,7 +643,7 @@
 status_t AudioTrack::getPosition(uint32_t *position)
 {
     if (position == 0) return BAD_VALUE;
-
+    AutoMutex lock(mLock);
     *position = mCblk->server;
 
     return NO_ERROR;
@@ -622,9 +651,11 @@
 
 status_t AudioTrack::reload()
 {
+    AutoMutex lock(mLock);
+
     if (!stopped()) return INVALID_OPERATION;
 
-    flush();
+    flush_l();
 
     mCblk->stepUser(mCblk->frameCount);
 
@@ -633,6 +664,13 @@
 
 audio_io_handle_t AudioTrack::getOutput()
 {
+    AutoMutex lock(mLock);
+    return getOutput_l();
+}
+
+// must be called with mLock held
+audio_io_handle_t AudioTrack::getOutput_l()
+{
     return AudioSystem::getOutput((AudioSystem::stream_type)mStreamType,
             mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags);
 }
@@ -654,7 +692,8 @@
 
 // -------------------------------------------------------------------------
 
-status_t AudioTrack::createTrack(
+// must be called with mLock held
+status_t AudioTrack::createTrack_l(
         int streamType,
         uint32_t sampleRate,
         int format,
@@ -774,6 +813,7 @@
 
 status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
 {
+    AutoMutex lock(mLock);
     int active;
     status_t result;
     audio_track_cblk_t* cblk = mCblk;
@@ -800,12 +840,17 @@
                 return WOULD_BLOCK;
             }
             if (!(cblk->flags & CBLK_INVALID_MSK)) {
+                mLock.unlock();
                 result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
-            }
-            if (cblk->flags & CBLK_INVALID_MSK) {
-                LOGW("obtainBuffer() track %p invalidated, creating a new one", this);
-                // no need to clear the invalid flag as this cblk will not be used anymore
                 cblk->lock.unlock();
+                mLock.lock();
+                if (mActive == 0) {
+                    return status_t(STOPPED);
+                }
+                cblk->lock.lock();
+            }
+
+            if (cblk->flags & CBLK_INVALID_MSK) {
                 goto create_new_track;
             }
             if (__builtin_expect(result!=NO_ERROR, false)) {
@@ -819,18 +864,17 @@
                         //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
                         cblk->lock.unlock();
                         result = mAudioTrack->start();
-                        if (result == DEAD_OBJECT) {
-                            LOGW("obtainBuffer() dead IAudioTrack: creating a new one");
-create_new_track:
-                            result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount,
-                                                 mFrameCount, mFlags, mSharedBuffer, getOutput(), false);
-                            if (result == NO_ERROR) {
-                                cblk = mCblk;
-                                cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
-                                mAudioTrack->start();
-                            }
-                        }
                         cblk->lock.lock();
+                        if (result == DEAD_OBJECT) {
+                            cblk->flags |= CBLK_INVALID_MSK;
+create_new_track:
+                            result = restoreTrack_l(cblk, false);
+                        }
+                        if (result != NO_ERROR) {
+                            LOGW("obtainBuffer create Track error %d", result);
+                            cblk->lock.unlock();
+                            return result;
+                        }
                     }
                     cblk->waitTimeMs = 0;
                 }
@@ -848,7 +892,7 @@
     }
 
     // restart track if it was disabled by audioflinger due to previous underrun
-    if (cblk->flags & CBLK_DISABLED_MSK) {
+    if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
         cblk->flags &= ~CBLK_DISABLED_ON;
         LOGW("obtainBuffer() track %p disabled, restarting", this);
         mAudioTrack->start();
@@ -883,8 +927,8 @@
 
 void AudioTrack::releaseBuffer(Buffer* audioBuffer)
 {
-    audio_track_cblk_t* cblk = mCblk;
-    cblk->stepUser(audioBuffer->frameCount);
+    AutoMutex lock(mLock);
+    mCblk->stepUser(audioBuffer->frameCount);
 }
 
 // -------------------------------------------------------------------------
@@ -903,6 +947,13 @@
 
     LOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive);
 
+    // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
+    // while we are accessing the cblk
+    mLock.lock();
+    sp <IAudioTrack> audioTrack = mAudioTrack;
+    sp <IMemory> iMem = mCblkMemory;
+    mLock.unlock();
+
     ssize_t written = 0;
     const int8_t *src = (const int8_t *)buffer;
     Buffer audioBuffer;
@@ -953,21 +1004,29 @@
     uint32_t frames;
     size_t writtenSize;
 
+    mLock.lock();
+    // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
+    // while we are accessing the cblk
+    sp <IAudioTrack> audioTrack = mAudioTrack;
+    sp <IMemory> iMem = mCblkMemory;
+    audio_track_cblk_t* cblk = mCblk;
+    mLock.unlock();
+
     // Manage underrun callback
-    if (mActive && (mCblk->framesReady() == 0)) {
-        LOGV("Underrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags);
-        if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
+    if (mActive && (cblk->framesReady() == 0)) {
+        LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
+        if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
             mCbf(EVENT_UNDERRUN, mUserData, 0);
-            if (mCblk->server == mCblk->frameCount) {
+            if (cblk->server == cblk->frameCount) {
                 mCbf(EVENT_BUFFER_END, mUserData, 0);
             }
-            mCblk->flags |= CBLK_UNDERRUN_ON;
+            cblk->flags |= CBLK_UNDERRUN_ON;
             if (mSharedBuffer != 0) return false;
         }
     }
 
     // Manage loop end callback
-    while (mLoopCount > mCblk->loopCount) {
+    while (mLoopCount > cblk->loopCount) {
         int loopCount = -1;
         mLoopCount--;
         if (mLoopCount >= 0) loopCount = mLoopCount;
@@ -977,7 +1036,7 @@
 
     // Manage marker callback
     if (!mMarkerReached && (mMarkerPosition > 0)) {
-        if (mCblk->server >= mMarkerPosition) {
+        if (cblk->server >= mMarkerPosition) {
             mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
             mMarkerReached = true;
         }
@@ -985,7 +1044,7 @@
 
     // Manage new position callback
     if (mUpdatePeriod > 0) {
-        while (mCblk->server >= mNewPosition) {
+        while (cblk->server >= mNewPosition) {
             mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
             mNewPosition += mUpdatePeriod;
         }
@@ -1068,6 +1127,84 @@
     return true;
 }
 
+// must be called with mLock and cblk.lock held. Callers must also hold strong references on
+// the IAudioTrack and IMemory in case they are recreated here.
+// If the IAudioTrack is successfully restored, the cblk pointer is updated
+status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart)
+{
+    status_t result;
+
+    if (!(cblk->flags & CBLK_RESTORING_MSK)) {
+        LOGW("dead IAudioTrack, creating a new one from %s",
+             fromStart ? "start()" : "obtainBuffer()");
+
+        cblk->flags |= CBLK_RESTORING_ON;
+        // signal old cblk condition so that other threads waiting for available buffers stop
+        // waiting now
+        cblk->cv.broadcast();
+        cblk->lock.unlock();
+
+        // if the new IAudioTrack is created, createTrack_l() will modify the
+        // following member variables: mAudioTrack, mCblkMemory and mCblk.
+        // It will also delete the strong references on previous IAudioTrack and IMemory
+        result = createTrack_l(mStreamType,
+                               cblk->sampleRate,
+                               mFormat,
+                               mChannelCount,
+                               mFrameCount,
+                               mFlags,
+                               mSharedBuffer,
+                               getOutput_l(),
+                               false);
+
+        if (result == NO_ERROR) {
+            if (!fromStart) {
+                mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+            }
+            result = mAudioTrack->start();
+            if (fromStart && result == NO_ERROR) {
+                mNewPosition = mCblk->server + mUpdatePeriod;
+            }
+        }
+        if (result != NO_ERROR) {
+            mActive = false;
+        }
+
+        // signal old cblk condition for other threads waiting for restore completion
+        cblk->lock.lock();
+        cblk->flags |= CBLK_RESTORED_MSK;
+        cblk->cv.broadcast();
+        cblk->lock.unlock();
+    } else {
+        if (!(cblk->flags & CBLK_RESTORED_MSK)) {
+            LOGW("dead IAudioTrack, waiting for a new one");
+            mLock.unlock();
+            result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS));
+            cblk->lock.unlock();
+            mLock.lock();
+        } else {
+            LOGW("dead IAudioTrack, already restored");
+            result = NO_ERROR;
+            cblk->lock.unlock();
+        }
+        if (result != NO_ERROR || mActive == 0) {
+            result = status_t(STOPPED);
+        }
+    }
+    LOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
+         result, mActive, mCblk, cblk, mCblk->flags, cblk->flags);
+
+    if (result == NO_ERROR) {
+        // from now on we switch to the newly created cblk
+        cblk = mCblk;
+    }
+    cblk->lock.lock();
+
+    LOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result);
+
+    return result;
+}
+
 status_t AudioTrack::dump(int fd, const Vector<String16>& args) const
 {
 
@@ -1197,7 +1334,9 @@
 
     this->server = s;
 
-    cv.signal();
+    if (!(flags & CBLK_INVALID_MSK)) {
+        cv.signal();
+    }
     lock.unlock();
     return true;
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d3d1750..fee245f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -8,6 +8,7 @@
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     <uses-permission android:name="android.permission.GET_TASKS" />
+    <uses-permission android:name="android.permission.MANAGE_USB" />
 
     <application
         android:persistent="true"
@@ -39,5 +40,22 @@
             android:exported="true">
         </activity>
 
+        <!-- started from UsbDeviceSettingsManager -->
+        <activity android:name=".usb.UsbPermissionActivity"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_USB"
+            android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+            android:finishOnCloseSystemDialogs="true"
+            android:excludeFromRecents="true">
+        </activity>
+
+        <!-- started from UsbDeviceSettingsManager -->
+        <activity android:name=".usb.UsbResolverActivity"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_USB"
+            android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+            android:finishOnCloseSystemDialogs="true"
+            android:excludeFromRecents="true">
+        </activity>
     </application>
 </manifest>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
index bfa6c36..3f172e6 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
@@ -38,8 +38,8 @@
         android:layout_height="wrap_content"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
-        android:layout_marginLeft="123dip"
-        android:layout_marginTop="16dip"
+        android:layout_marginLeft="131dip"
+        android:layout_marginTop="13dip"
         android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
         android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
         android:adjustViewBounds="true"
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
index eda19b7..42940be 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
@@ -51,7 +51,7 @@
                 android:stackFromBottom="true"
                 android:fadingEdge="vertical"
                 android:scrollbars="none"
-                android:fadingEdgeLength="30dip"
+                android:fadingEdgeLength="20dip"
                 android:listSelector="@drawable/recents_thumbnail_bg_selector"
             />
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ebd48e7..becad6a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -115,4 +115,11 @@
 
     <!-- Label of a toggle switch to disable use of the physical keyboard in favor of the IME. [CHAR LIMIT=25] -->
     <string name="status_bar_use_physical_keyboard">Use physical keyboard</string>
+
+    <!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
+    <string name="usb_device_permission_prompt">Allow the application %1$s to access the USB device?</string>
+
+    <!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
+    <string name="usb_accessory_permission_prompt">Allow the application %1$s to access the USB accessory?</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java
new file mode 100644
index 0000000..1edebbb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.usb;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+// This class is used to close UsbPermissionsActivity and UsbResolverActivity
+// if their device/accessory is disconnected while the dialog is still open
+class UsbDisconnectedReceiver extends BroadcastReceiver {
+    private final Activity mActivity;
+    private UsbDevice mDevice;
+    private UsbAccessory mAccessory;
+
+    public UsbDisconnectedReceiver(Activity activity, UsbDevice device) {
+       mActivity = activity;
+        mDevice = device;
+
+        IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        activity.registerReceiver(this, filter);
+    }
+
+    public UsbDisconnectedReceiver(Activity activity, UsbAccessory accessory) {
+        mActivity = activity;
+        mAccessory = accessory;
+
+        IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+        activity.registerReceiver(this, filter);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+            UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+            if (device != null && device.equals(mDevice)) {
+                mActivity.finish();
+            }
+        } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
+            UsbAccessory accessory =
+                    (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+            if (accessory != null && accessory.equals(mAccessory)) {
+                mActivity.finish();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
new file mode 100644
index 0000000..f1784df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.usb;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import com.android.systemui.R;
+
+public class UsbPermissionActivity extends AlertActivity
+        implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
+
+    private static final String TAG = "UsbPermissionActivity";
+
+    private CheckBox mAlwaysCheck;
+    private TextView mClearDefaultHint;
+    private UsbDevice mDevice;
+    private UsbAccessory mAccessory;
+    private PendingIntent mPendingIntent;
+    private String mPackageName;
+    private int mUid;
+    private boolean mPermissionGranted;
+    private UsbDisconnectedReceiver mDisconnectedReceiver;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+       Intent intent = getIntent();
+        mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+        mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+        mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
+        mUid = intent.getIntExtra("uid", 0);
+        mPackageName = intent.getStringExtra("package");
+
+        PackageManager packageManager = getPackageManager();
+        ApplicationInfo aInfo;
+        try {
+            aInfo = packageManager.getApplicationInfo(mPackageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "unable to look up package name", e);
+            finish();
+            return;
+        }
+        String appName = aInfo.loadLabel(packageManager).toString();
+
+        final AlertController.AlertParams ap = mAlertParams;
+        ap.mIcon = aInfo.loadIcon(packageManager);
+        ap.mTitle = appName;
+        if (mDevice == null) {
+            ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName);
+            mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
+        } else {
+            ap.mMessage = getString(R.string.usb_device_permission_prompt, appName);
+            mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
+        }
+        ap.mPositiveButtonText = getString(com.android.internal.R.string.ok);
+        ap.mNegativeButtonText = getString(com.android.internal.R.string.cancel);
+        ap.mPositiveButtonListener = this;
+        ap.mNegativeButtonListener = this;
+
+        // add "always use" checkbox
+        LayoutInflater inflater = (LayoutInflater)getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+        mAlwaysCheck = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+        mAlwaysCheck.setText(com.android.internal.R.string.alwaysUse);
+        mAlwaysCheck.setOnCheckedChangeListener(this);
+        mClearDefaultHint = (TextView)ap.mView.findViewById(
+                                                    com.android.internal.R.id.clearDefaultHint);
+        mClearDefaultHint.setVisibility(View.GONE);
+
+        setupAlert();
+
+    }
+
+    @Override
+    public void onDestroy() {
+        IBinder b = ServiceManager.getService(USB_SERVICE);
+        IUsbManager service = IUsbManager.Stub.asInterface(b);
+
+        // send response via pending intent
+        Intent intent = new Intent();
+        try {
+            if (mDevice != null) {
+                intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
+                if (mPermissionGranted) {
+                    service.grantDevicePermission(mDevice, mUid);
+                    if (mAlwaysCheck.isChecked()) {
+                        service.setDevicePackage(mDevice, mPackageName);
+                    }
+                }
+            }
+            if (mAccessory != null) {
+                intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
+                if (mPermissionGranted) {
+                    service.grantAccessoryPermission(mAccessory, mUid);
+                    if (mAlwaysCheck.isChecked()) {
+                        service.setAccessoryPackage(mAccessory, mPackageName);
+                    }
+                }
+            }
+            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted);
+            mPendingIntent.send(this, 0, intent);
+        } catch (PendingIntent.CanceledException e) {
+            Log.w(TAG, "PendingIntent was cancelled");
+        } catch (RemoteException e) {
+            Log.e(TAG, "IUsbService connection failed", e);
+        }
+
+        if (mDisconnectedReceiver != null) {
+            unregisterReceiver(mDisconnectedReceiver);
+        }
+        super.onDestroy();
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == AlertDialog.BUTTON_POSITIVE) {
+            mPermissionGranted = true;
+        }
+        finish();
+    }
+
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (mClearDefaultHint == null) return;
+
+        if(isChecked) {
+            mClearDefaultHint.setVisibility(View.VISIBLE);
+        } else {
+            mClearDefaultHint.setVisibility(View.GONE);
+        }
+    }
+}
diff --git a/services/java/com/android/server/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
similarity index 70%
rename from services/java/com/android/server/usb/UsbResolverActivity.java
rename to packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index e8a09a5..84d73dd 100644
--- a/services/java/com/android/server/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.usb;
+package com.android.systemui.usb;
 
 import com.android.internal.app.ResolverActivity;
 
@@ -39,6 +39,10 @@
     public static final String TAG = "UsbResolverActivity";
     public static final String EXTRA_RESOLVE_INFOS = "rlist";
 
+    private UsbDevice mDevice;
+    private UsbAccessory mAccessory;
+    private UsbDisconnectedReceiver mDisconnectedReceiver;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Intent intent = getIntent();
@@ -50,7 +54,6 @@
         }
         Intent target = (Intent)targetParcelable;
         ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS);
-        Log.d(TAG, "rList.size() " + rList.size());
         CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
         super.onCreate(savedInstanceState, target, title, null, rList,
                 true, /* Set alwaysUseOption to true to enable "always use this app" checkbox. */
@@ -58,6 +61,27 @@
                          This is necessary because this activity is needed for the user to allow
                          the application permission to access the device */
                 );
+
+        mDevice = (UsbDevice)target.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+        if (mDevice != null) {
+            mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
+        } else {
+            mAccessory = (UsbAccessory)target.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+            if (mAccessory == null) {
+                Log.e(TAG, "no device or accessory");
+                finish();
+                return;
+            }
+            mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mDisconnectedReceiver != null) {
+            unregisterReceiver(mDisconnectedReceiver);
+        }
+        super.onDestroy();
     }
 
     protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
@@ -65,28 +89,24 @@
             IBinder b = ServiceManager.getService(USB_SERVICE);
             IUsbManager service = IUsbManager.Stub.asInterface(b);
             int uid = ri.activityInfo.applicationInfo.uid;
-            String action = intent.getAction();
 
-            if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
-                UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+            if (mDevice != null) {
                 // grant permission for the device
-                service.grantDevicePermission(device, uid);
+                service.grantDevicePermission(mDevice, uid);
                 // set or clear default setting
                 if (alwaysCheck) {
-                    service.setDevicePackage(device, ri.activityInfo.packageName);
+                    service.setDevicePackage(mDevice, ri.activityInfo.packageName);
                 } else {
-                    service.setDevicePackage(device, null);
+                    service.setDevicePackage(mDevice, null);
                 }
-            } else if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
-                UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(
-                        UsbManager.EXTRA_ACCESSORY);
+            } else if (mAccessory != null) {
                 // grant permission for the accessory
-                service.grantAccessoryPermission(accessory, uid);
+                service.grantAccessoryPermission(mAccessory, uid);
                 // set or clear default setting
                 if (alwaysCheck) {
-                    service.setAccessoryPackage(accessory, ri.activityInfo.packageName);
+                    service.setAccessoryPackage(mAccessory, ri.activityInfo.packageName);
                 } else {
-                    service.setAccessoryPackage(accessory, null);
+                    service.setAccessoryPackage(mAccessory, null);
                 }
             }
 
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index adc49ae..3c6c427 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -62,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -99,9 +100,6 @@
     /* Chipset supports background scan */
     private final boolean mBackgroundScanSupported;
 
-    // true if the user enabled Wifi while in airplane mode
-    private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false);
-
     private final LockList mLocks = new LockList();
     // some wifi lock statistics
     private int mFullHighPerfLocksAcquired;
@@ -144,6 +142,14 @@
     private static final String ACTION_DEVICE_IDLE =
             "com.android.server.WifiManager.action.DEVICE_IDLE";
 
+    private static final int WIFI_DISABLED                  = 0;
+    private static final int WIFI_ENABLED                   = 1;
+    /* Wifi enabled while in airplane mode */
+    private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2;
+
+    private AtomicInteger mWifiState = new AtomicInteger(WIFI_DISABLED);
+    private AtomicBoolean mAirplaneModeOn = new AtomicBoolean(false);
+
     private boolean mIsReceiverRegistered = false;
 
 
@@ -338,11 +344,11 @@
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
-                        // clear our flag indicating the user has overwridden airplane mode
-                        mAirplaneModeOverwridden.set(false);
-                        // on airplane disable, restore Wifi if the saved state indicates so
-                        if (!isAirplaneModeOn() && testAndClearWifiSavedState()) {
-                            persistWifiEnabled(true);
+                        mAirplaneModeOn.set(isAirplaneModeOn());
+                        /* On airplane mode disable, restore wifi state if necessary */
+                        if (!mAirplaneModeOn.get() && (testAndClearWifiSavedState() ||
+                            mWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE)) {
+                                persistWifiEnabled(true);
                         }
                         updateWifiState();
                     }
@@ -402,9 +408,10 @@
      * This function is used only at boot time
      */
     public void checkAndStartWifi() {
-        /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
-        boolean wifiEnabled = !isAirplaneModeOn()
-                && (getPersistedWifiEnabled() || testAndClearWifiSavedState());
+        mAirplaneModeOn.set(isAirplaneModeOn());
+        mWifiState.set(getPersistedWifiState());
+        /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */
+        boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
         Slog.i(TAG, "WifiService starting up with Wi-Fi " +
                 (wifiEnabled ? "enabled" : "disabled"));
         setWifiEnabled(wifiEnabled);
@@ -423,21 +430,39 @@
         return (wifiSavedState == 1);
     }
 
-    private boolean getPersistedWifiEnabled() {
+    private int getPersistedWifiState() {
         final ContentResolver cr = mContext.getContentResolver();
         try {
-            return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
+            return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON);
         } catch (Settings.SettingNotFoundException e) {
-            Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
-            return false;
+            Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, WIFI_DISABLED);
+            return WIFI_DISABLED;
+        }
+    }
+
+    private boolean shouldWifiBeEnabled() {
+        if (mAirplaneModeOn.get()) {
+            return mWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE;
+        } else {
+            return mWifiState.get() != WIFI_DISABLED;
         }
     }
 
     private void persistWifiEnabled(boolean enabled) {
         final ContentResolver cr = mContext.getContentResolver();
-        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
+        if (enabled) {
+            if (isAirplaneModeOn() && isAirplaneToggleable()) {
+                mWifiState.set(WIFI_ENABLED_AIRPLANE_OVERRIDE);
+            } else {
+                mWifiState.set(WIFI_ENABLED);
+            }
+        } else {
+            mWifiState.set(WIFI_DISABLED);
+        }
+        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, mWifiState.get());
     }
 
+
     /**
      * see {@link android.net.wifi.WifiManager#pingSupplicant()}
      * @return {@code true} if the operation succeeds, {@code false} otherwise
@@ -490,11 +515,6 @@
             Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
         }
 
-        // set a flag if the user is enabling Wifi while in airplane mode
-        if (enable && isAirplaneModeOn() && isAirplaneToggleable()) {
-            mAirplaneModeOverwridden.set(true);
-        }
-
         if (enable) {
             reportStartWorkSource();
         }
@@ -1037,11 +1057,8 @@
     }
 
     private void updateWifiState() {
-        boolean wifiEnabled = getPersistedWifiEnabled();
-        boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get();
         boolean lockHeld = mLocks.hasLocks();
         int strongestLockMode = WifiManager.WIFI_MODE_FULL;
-        boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
         boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
 
         if (lockHeld) {
@@ -1053,11 +1070,11 @@
         }
 
         /* Disable tethering when airplane mode is enabled */
-        if (airplaneMode) {
+        if (mAirplaneModeOn.get()) {
             mWifiStateMachine.setWifiApEnabled(null, false);
         }
 
-        if (wifiShouldBeEnabled) {
+        if (shouldWifiBeEnabled()) {
             if (wifiShouldBeStarted) {
                 reportStartWorkSource();
                 mWifiStateMachine.setWifiEnabled(true);
diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
index 2f22fe1..25eac6f 100644
--- a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
@@ -16,11 +16,13 @@
 
 package com.android.server.usb;
 
+import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
@@ -33,7 +35,7 @@
 import android.os.FileUtils;
 import android.os.Process;
 import android.util.Log;
-import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.Xml;
 
 import com.android.internal.content.PackageMonitor;
@@ -63,16 +65,16 @@
 
     private final Context mContext;
 
-    // maps UID to user approved USB devices
-    private final SparseArray<ArrayList<DeviceFilter>> mDevicePermissionMap =
-            new SparseArray<ArrayList<DeviceFilter>>();
-    // maps UID to user approved USB accessories
-    private final SparseArray<ArrayList<AccessoryFilter>> mAccessoryPermissionMap =
-            new SparseArray<ArrayList<AccessoryFilter>>();
+    // Temporary mapping USB device name to list of UIDs with permissions for the device
+    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
+            new HashMap<String, SparseBooleanArray>();
+    // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
+    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+            new HashMap<UsbAccessory, SparseBooleanArray>();
     // Maps DeviceFilter to user preferred application package
     private final HashMap<DeviceFilter, String> mDevicePreferenceMap =
             new HashMap<DeviceFilter, String>();
-    // Maps DeviceFilter to user preferred application package
+    // Maps AccessoryFilter to user preferred application package
     private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
             new HashMap<AccessoryFilter, String>();
 
@@ -352,15 +354,6 @@
                 }
             }
         }
-
-        public void onUidRemoved(int uid) {
-            synchronized (mLock) {
-                // clear all permissions for the UID
-                if (clearUidDefaultsLocked(uid)) {
-                    writeSettingsLocked();
-                }
-            }
-        }
     }
     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
@@ -372,44 +365,6 @@
         mPackageMonitor.register(context, true);
     }
 
-    private void readDevicePermission(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        int uid = -1;
-        ArrayList<DeviceFilter> filters = new ArrayList<DeviceFilter>();
-        int count = parser.getAttributeCount();
-        for (int i = 0; i < count; i++) {
-            if ("uid".equals(parser.getAttributeName(i))) {
-                uid = Integer.parseInt(parser.getAttributeValue(i));
-                break;
-            }
-        }
-        XmlUtils.nextElement(parser);
-        while ("usb-device".equals(parser.getName())) {
-            filters.add(DeviceFilter.read(parser));
-            XmlUtils.nextElement(parser);
-        }
-        mDevicePermissionMap.put(uid, filters);
-    }
-
-    private void readAccessoryPermission(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        int uid = -1;
-        ArrayList<AccessoryFilter> filters = new ArrayList<AccessoryFilter>();
-        int count = parser.getAttributeCount();
-        for (int i = 0; i < count; i++) {
-            if ("uid".equals(parser.getAttributeName(i))) {
-                uid = Integer.parseInt(parser.getAttributeValue(i));
-                break;
-            }
-        }
-        XmlUtils.nextElement(parser);
-        while ("usb-accessory".equals(parser.getName())) {
-            filters.add(AccessoryFilter.read(parser));
-            XmlUtils.nextElement(parser);
-        }
-        mAccessoryPermissionMap.put(uid, filters);
-    }
-
     private void readPreference(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         String packageName = null;
@@ -441,11 +396,7 @@
             XmlUtils.nextElement(parser);
             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
                 String tagName = parser.getName();
-                if ("device-permission".equals(tagName)) {
-                    readDevicePermission(parser);
-                } else if ("accessory-permission".equals(tagName)) {
-                    readAccessoryPermission(parser);
-                } else if ("preference".equals(tagName)) {
+                if ("preference".equals(tagName)) {
                     readPreference(parser);
                  } else {
                     XmlUtils.nextElement(parser);
@@ -478,32 +429,6 @@
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             serializer.startTag(null, "settings");
 
-            int count = mDevicePermissionMap.size();
-            for (int i = 0; i < count; i++) {
-                int uid = mDevicePermissionMap.keyAt(i);
-                ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i);
-                serializer.startTag(null, "device-permission");
-                serializer.attribute(null, "uid", Integer.toString(uid));
-                int filterCount = filters.size();
-                for (int j = 0; j < filterCount; j++) {
-                    filters.get(j).write(serializer);
-                }
-                serializer.endTag(null, "device-permission");
-            }
-
-            count = mAccessoryPermissionMap.size();
-            for (int i = 0; i < count; i++) {
-                int uid = mAccessoryPermissionMap.keyAt(i);
-                ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
-                serializer.startTag(null, "accessory-permission");
-                serializer.attribute(null, "uid", Integer.toString(uid));
-                int filterCount = filters.size();
-                for (int j = 0; j < filterCount; j++) {
-                    filters.get(j).write(serializer);
-                }
-                serializer.endTag(null, "accessory-permission");
-            }
-
             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
                 serializer.startTag(null, "preference");
                 serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
@@ -602,53 +527,26 @@
     }
 
     public void deviceAttached(UsbDevice device) {
-        Intent deviceIntent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
-        deviceIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        deviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         ArrayList<ResolveInfo> matches;
         String defaultPackage;
         synchronized (mLock) {
-            matches = getDeviceMatchesLocked(device, deviceIntent);
+            matches = getDeviceMatchesLocked(device, intent);
             // Launch our default activity directly, if we have one.
             // Otherwise we will start the UsbResolverActivity to allow the user to choose.
             defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
         }
 
-        int count = matches.size();
-        // don't show the resolver activity if there are no choices available
-        if (count == 0) return;
-
-        if (defaultPackage != null) {
-            for (int i = 0; i < count; i++) {
-                ResolveInfo rInfo = matches.get(i);
-                if (rInfo.activityInfo != null &&
-                        defaultPackage.equals(rInfo.activityInfo.packageName)) {
-                    try {
-                        deviceIntent.setComponent(new ComponentName(
-                                defaultPackage, rInfo.activityInfo.name));
-                        mContext.startActivity(deviceIntent);
-                    } catch (ActivityNotFoundException e) {
-                        Log.e(TAG, "startActivity failed", e);
-                    }
-                    return;
-                }
-            }
-        }
-
-        Intent intent = new Intent(mContext, UsbResolverActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        intent.putExtra(Intent.EXTRA_INTENT, deviceIntent);
-        intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches);
-        try {
-            mContext.startActivity(intent);
-        } catch (ActivityNotFoundException e) {
-            Log.w(TAG, "unable to start UsbResolverActivity");
-        }
+        resolveActivity(intent, matches, defaultPackage, device, null);
     }
 
     public void deviceDetached(UsbDevice device) {
+        // clear temporary permissions for the device
+        mDevicePermissionMap.remove(device.getDeviceName());
+
         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
         Log.d(TAG, "usbDeviceRemoved, sending " + intent);
@@ -656,93 +554,198 @@
     }
 
     public void accessoryAttached(UsbAccessory accessory) {
-        Intent accessoryIntent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
-        accessoryIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        accessoryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         ArrayList<ResolveInfo> matches;
         String defaultPackage;
         synchronized (mLock) {
-            matches = getAccessoryMatchesLocked(accessory, accessoryIntent);
+            matches = getAccessoryMatchesLocked(accessory, intent);
             // Launch our default activity directly, if we have one.
             // Otherwise we will start the UsbResolverActivity to allow the user to choose.
             defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
         }
 
-        int count = matches.size();
-        // don't show the resolver activity if there are no choices available
-        if (count == 0) return;
-
-        if (defaultPackage != null) {
-            for (int i = 0; i < count; i++) {
-                ResolveInfo rInfo = matches.get(i);
-                if (rInfo.activityInfo != null &&
-                        defaultPackage.equals(rInfo.activityInfo.packageName)) {
-                    try {
-                        accessoryIntent.setComponent(new ComponentName(
-                                defaultPackage, rInfo.activityInfo.name));
-                        mContext.startActivity(accessoryIntent);
-                    } catch (ActivityNotFoundException e) {
-                        Log.e(TAG, "startActivity failed", e);
-                    }
-                    return;
-                }
-            }
-        }
-
-        Intent intent = new Intent(mContext, UsbResolverActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        intent.putExtra(Intent.EXTRA_INTENT, accessoryIntent);
-        intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches);
-        try {
-            mContext.startActivity(intent);
-        } catch (ActivityNotFoundException e) {
-            Log.w(TAG, "unable to start UsbResolverActivity");
-        }
+        resolveActivity(intent, matches, defaultPackage, null, accessory);
     }
 
     public void accessoryDetached(UsbAccessory accessory) {
+        // clear temporary permissions for the accessory
+        mAccessoryPermissionMap.remove(accessory);
+
         Intent intent = new Intent(
                 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
         mContext.sendBroadcast(intent);
     }
 
-    public void checkPermission(UsbDevice device) {
-        if (device == null) return;
-        synchronized (mLock) {
-            ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(Binder.getCallingUid());
-            if (filterList != null) {
-                int count = filterList.size();
-                for (int i = 0; i < count; i++) {
-                    DeviceFilter filter = filterList.get(i);
-                    if (filter.equals(device)) {
-                        // permission allowed
-                        return;
-                    }
+    private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
+            String defaultPackage, UsbDevice device, UsbAccessory accessory) {
+        int count = matches.size();
+        // don't show the resolver activity if there are no choices available
+        if (count == 0) return;
+
+        ResolveInfo defaultRI = null;
+        if (count == 1 && defaultPackage == null) {
+            // Check to see if our single choice is on the system partition.
+            // If so, treat it as our default without calling UsbResolverActivity
+            ResolveInfo rInfo = matches.get(0);
+            if (rInfo.activityInfo != null &&
+                    rInfo.activityInfo.applicationInfo != null &&
+                    (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                defaultRI = rInfo;
+            }
+        }
+
+        if (defaultRI == null && defaultPackage != null) {
+            // look for default activity
+            for (int i = 0; i < count; i++) {
+                ResolveInfo rInfo = matches.get(i);
+                if (rInfo.activityInfo != null &&
+                        defaultPackage.equals(rInfo.activityInfo.packageName)) {
+                    defaultRI = rInfo;
+                    break;
                 }
             }
         }
-        throw new SecurityException("User has not given permission to device " + device);
+
+        if (defaultRI != null) {
+            // grant permission for default activity
+            if (device != null) {
+                grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
+            } else if (accessory != null) {
+                grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid);
+            }
+
+            // start default activity directly
+            try {
+                intent.setComponent(
+                        new ComponentName(defaultRI.activityInfo.packageName,
+                                defaultRI.activityInfo.name));
+                mContext.startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                Log.e(TAG, "startActivity failed", e);
+            }
+        } else {
+            long identity = Binder.clearCallingIdentity();
+
+            // start UsbResolverActivity so user can choose an activity
+            Intent resolverIntent = new Intent();
+            resolverIntent.setClassName("com.android.systemui",
+                    "com.android.systemui.usb.UsbResolverActivity");
+            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+            resolverIntent.putParcelableArrayListExtra("rlist", matches);
+            try {
+                mContext.startActivity(resolverIntent);
+            } catch (ActivityNotFoundException e) {
+                Log.e(TAG, "unable to start UsbResolverActivity");
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    public boolean hasPermission(UsbDevice device) {
+        synchronized (mLock) {
+            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(Binder.getCallingUid());
+        }
+    }
+
+    public boolean hasPermission(UsbAccessory accessory) {
+        synchronized (mLock) {
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(Binder.getCallingUid());
+        }
+    }
+
+    public void checkPermission(UsbDevice device) {
+        if (!hasPermission(device)) {
+            throw new SecurityException("User has not given permission to device " + device);
+        }
     }
 
     public void checkPermission(UsbAccessory accessory) {
-        if (accessory == null) return;
-        synchronized (mLock) {
-            ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid());
-            if (filterList != null) {
-                int count = filterList.size();
-                for (int i = 0; i < count; i++) {
-                    AccessoryFilter filter = filterList.get(i);
-                    if (filter.equals(accessory)) {
-                        // permission allowed
-                        return;
-                    }
-                }
-            }
+        if (!hasPermission(accessory)) {
+            throw new SecurityException("User has not given permission to accessory " + accessory);
         }
-        throw new SecurityException("User has not given permission to accessory " + accessory);
+    }
+
+    private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
+        int uid = Binder.getCallingUid();
+
+        // compare uid with packageName to foil apps pretending to be someone else
+        try {
+            ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+            if (aInfo.uid != uid) {
+                throw new IllegalArgumentException("package " + packageName +
+                        " does not match caller's uid " + uid);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("package " + packageName + " not found");
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        intent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbPermissionActivity");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(Intent.EXTRA_INTENT, pi);
+        intent.putExtra("package", packageName);
+        intent.putExtra("uid", uid);
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "unable to start UsbPermissionActivity");
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
+      Intent intent = new Intent();
+
+        // respond immediately if permission has already been granted
+      if (hasPermission(device)) {
+            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+            try {
+                pi.send(mContext, 0, intent);
+            } catch (PendingIntent.CanceledException e) {
+                Log.w(TAG, "requestPermission PendingIntent was cancelled");
+            }
+            return;
+        }
+
+        // start UsbPermissionActivity so user can choose an activity
+        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        requestPermissionDialog(intent, packageName, pi);
+    }
+
+    public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
+      Intent intent = new Intent();
+
+        // respond immediately if permission has already been granted
+        if (hasPermission(accessory)) {
+            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+           try {
+                pi.send(mContext, 0, intent);
+            } catch (PendingIntent.CanceledException e) {
+                Log.w(TAG, "requestPermission PendingIntent was cancelled");
+            }
+            return;
+        }
+
+        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        requestPermissionDialog(intent, packageName, pi);
     }
 
     public void setDevicePackage(UsbDevice device, String packageName) {
@@ -783,73 +786,43 @@
 
     public void grantDevicePermission(UsbDevice device, int uid) {
         synchronized (mLock) {
-            ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(uid);
-            if (filterList == null) {
-                filterList = new ArrayList<DeviceFilter>();
-                mDevicePermissionMap.put(uid, filterList);
-            } else {
-                int count = filterList.size();
-                for (int i = 0; i < count; i++) {
-                    if (filterList.get(i).equals(device)) return;
-                }
+            String deviceName = device.getDeviceName();
+            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mDevicePermissionMap.put(deviceName, uidList);
             }
-            filterList.add(new DeviceFilter(device));
-            writeSettingsLocked();
+            uidList.put(uid, true);
         }
     }
 
     public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
         synchronized (mLock) {
-            ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(uid);
-            if (filterList == null) {
-                filterList = new ArrayList<AccessoryFilter>();
-                mAccessoryPermissionMap.put(uid, filterList);
-            } else {
-                int count = filterList.size();
-                for (int i = 0; i < count; i++) {
-                    if (filterList.get(i).equals(accessory)) return;
-                }
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mAccessoryPermissionMap.put(accessory, uidList);
             }
-            filterList.add(new AccessoryFilter(accessory));
-            writeSettingsLocked();
+            uidList.put(uid, true);
         }
     }
 
-    public boolean hasDefaults(String packageName, int uid) {
+    public boolean hasDefaults(String packageName) {
         synchronized (mLock) {
-            if (mDevicePermissionMap.get(uid) != null) return true;
-            if (mAccessoryPermissionMap.get(uid) != null) return true;
             if (mDevicePreferenceMap.values().contains(packageName)) return true;
             if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
             return false;
         }
     }
 
-    public void clearDefaults(String packageName, int uid) {
+    public void clearDefaults(String packageName) {
         synchronized (mLock) {
-            boolean packageCleared = clearPackageDefaultsLocked(packageName);
-            boolean uidCleared = clearUidDefaultsLocked(uid);
-            if (packageCleared || uidCleared) {
+            if (clearPackageDefaultsLocked(packageName)) {
                 writeSettingsLocked();
             }
         }
     }
 
-    private boolean clearUidDefaultsLocked(int uid) {
-        boolean cleared = false;
-        int index = mDevicePermissionMap.indexOfKey(uid);
-        if (index >= 0) {
-            mDevicePermissionMap.removeAt(index);
-            cleared = true;
-        }
-        index = mAccessoryPermissionMap.indexOfKey(uid);
-        if (index >= 0) {
-            mAccessoryPermissionMap.removeAt(index);
-            cleared = true;
-        }
-        return cleared;
-    }
-
     private boolean clearPackageDefaultsLocked(String packageName) {
         boolean cleared = false;
         synchronized (mLock) {
@@ -882,24 +855,24 @@
     public void dump(FileDescriptor fd, PrintWriter pw) {
         synchronized (mLock) {
             pw.println("  Device permissions:");
-            int count = mDevicePermissionMap.size();
-            for (int i = 0; i < count; i++) {
-                int uid = mDevicePermissionMap.keyAt(i);
-                pw.println("    " + "uid " + uid + ":");
-                ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i);
-                for (DeviceFilter filter : filters) {
-                    pw.println("      " + filter);
+            for (String deviceName : mDevicePermissionMap.keySet()) {
+                pw.print("    " + deviceName + ": ");
+                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    pw.print(Integer.toString(uidList.keyAt(i)) + " ");
                 }
+                pw.println("");
             }
             pw.println("  Accessory permissions:");
-            count = mAccessoryPermissionMap.size();
-            for (int i = 0; i < count; i++) {
-                int uid = mAccessoryPermissionMap.keyAt(i);
-                pw.println("    " + "uid " + uid + ":");
-                ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
-                for (AccessoryFilter filter : filters) {
-                    pw.println("      " + filter);
+            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+                pw.print("    " + accessory + ": ");
+                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    pw.print(Integer.toString(uidList.keyAt(i)) + " ");
                 }
+                pw.println("");
             }
             pw.println("  Device preferences:");
             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 8170c61..71f2a9b 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usb;
 
+import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -454,6 +455,24 @@
         mDeviceManager.setAccessoryPackage(accessory, packageName);
     }
 
+    public boolean hasDevicePermission(UsbDevice device) {
+        return mDeviceManager.hasPermission(device);
+    }
+
+    public boolean hasAccessoryPermission(UsbAccessory accessory) {
+        return mDeviceManager.hasPermission(accessory);
+    }
+
+    public void requestDevicePermission(UsbDevice device, String packageName,
+            PendingIntent pi) {
+        mDeviceManager.requestPermission(device, packageName, pi);
+    }
+
+    public void requestAccessoryPermission(UsbAccessory accessory, String packageName,
+            PendingIntent pi) {
+        mDeviceManager.requestPermission(accessory, packageName, pi);
+    }
+
     public void grantDevicePermission(UsbDevice device, int uid) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
         mDeviceManager.grantDevicePermission(device, uid);
@@ -464,14 +483,14 @@
         mDeviceManager.grantAccessoryPermission(accessory, uid);
     }
 
-    public boolean hasDefaults(String packageName, int uid) {
+    public boolean hasDefaults(String packageName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        return mDeviceManager.hasDefaults(packageName, uid);
+        return mDeviceManager.hasDefaults(packageName);
     }
 
-    public void clearDefaults(String packageName, int uid) {
+    public void clearDefaults(String packageName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.clearDefaults(packageName, uid);
+        mDeviceManager.clearDefaults(packageName);
     }
 
     /*
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index ee5c1f03..33e6a36 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4799,13 +4799,17 @@
                 if (maxLayer < ws.mAnimLayer) {
                     maxLayer = ws.mAnimLayer;
                 }
-                final Rect wf = ws.mFrame;
-                final Rect cr = ws.mContentInsets;
-                int left = wf.left + cr.left;
-                int top = wf.top + cr.top;
-                int right = wf.right - cr.right;
-                int bottom = wf.bottom - cr.bottom;
-                frame.union(left, top, right, bottom);
+                
+                // Don't include wallpaper in bounds calculation
+                if (!ws.mIsWallpaper) {
+                    final Rect wf = ws.mFrame;
+                    final Rect cr = ws.mContentInsets;
+                    int left = wf.left + cr.left;
+                    int top = wf.top + cr.top;
+                    int right = wf.right - cr.right;
+                    int bottom = wf.bottom - cr.bottom;
+                    frame.union(left, top, right, bottom);
+                }
             }
             Binder.restoreCallingIdentity(ident);
 
@@ -5460,8 +5464,9 @@
             shortSize = (int)(shortSize/dm.density);
 
             // These semi-magic numbers define our compatibility modes for
-            // applications with different screens.  Don't change unless you
-            // make sure to test lots and lots of apps!
+            // applications with different screens.  These are guarantees to
+            // app developers about the space they can expect for a particular
+            // configuration.  DO NOT CHANGE!
             if (longSize < 470) {
                 // This is shorter than an HVGA normal density screen (which
                 // is 480 pixels on its long side).
@@ -5469,12 +5474,12 @@
                         | Configuration.SCREENLAYOUT_LONG_NO;
             } else {
                 // What size is this screen screen?
-                if (longSize >= 800 && shortSize >= 600) {
-                    // SVGA or larger screens at medium density are the point
+                if (longSize >= 960 && shortSize >= 720) {
+                    // 1.5xVGA or larger screens at medium density are the point
                     // at which we consider it to be an extra large screen.
                     mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
-                } else if (longSize >= 530 && shortSize >= 400) {
-                    // SVGA or larger screens at high density are the point
+                } else if (longSize >= 640 && shortSize >= 480) {
+                    // VGA or larger screens at medium density are the point
                     // at which we consider it to be a large screen.
                     mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
                 } else {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 3be3b1b..bd4e787 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -727,14 +727,14 @@
     };
 
     if (wmActions & WM_ACTION_GO_TO_SLEEP) {
-#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_INPUT_DISPATCHER_POLICY
         LOGD("handleInterceptActions: Going to sleep.");
 #endif
         android_server_PowerManagerService_goToSleep(when);
     }
 
     if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
-#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_INPUT_DISPATCHER_POLICY
         LOGD("handleInterceptActions: Poking user activity.");
 #endif
         android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
@@ -743,7 +743,7 @@
     if (wmActions & WM_ACTION_PASS_TO_USER) {
         policyFlags |= POLICY_FLAG_PASS_TO_USER;
     } else {
-#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_INPUT_DISPATCHER_POLICY
         LOGD("handleInterceptActions: Not passing key to user.");
 #endif
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 85bc785..db14e53 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -393,7 +393,7 @@
         for (int i = 0 ; i < 3 ; i++) {
             if (radii[i * 2] != radii[(i + 1) * 2] || radii[i * 2 + 1] != radii[(i + 1) * 2 + 1]) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                        "Different corner size is not support in Path.addRoundRect.",
+                        "Different corner sizes are not supported in Path.addRoundRect.",
                         null, null /*data*/);
                 break;
             }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index acc7379..e6e9647 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -192,7 +192,7 @@
                 Capability.UNBOUND_RENDERING,
                 Capability.CUSTOM_BACKGROUND_COLOR,
                 Capability.RENDER,
-                Capability.LAYOUT_ONLY,
+                //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this.
                 Capability.EMBEDDED_LAYOUT,
                 Capability.VIEW_MANIPULATION,
                 Capability.PLAY_ANIMATION,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index 77c1789..138a455 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -488,18 +488,29 @@
             return defValue;
         }
 
-        float f = getDimension(index, defValue);
-        final int res = (int)(f+0.5f);
-        if (res != 0) return res;
-        if (f == 0) return 0;
-        if (f > 0) return 1; // this is to support ]0;1[ range (since >=1 is handled 2 lines above)
-        if (f < 0) {
-            // negative values are not allowed in pixel dimensions
-            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                    "Negative pixel dimension: " + s,
-                    null, null /*data*/);
+        if (ResourceHelper.stringToFloat(s, mValue)) {
+            float f = mValue.getDimension(mBridgeResources.mMetrics);
+
+            if (f < 0) {
+                // negative values are not allowed in pixel dimensions
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Negative pixel dimension: " + s,
+                        null, null /*data*/);
+                return defValue;
+            }
+
+            if (f == 0) return 0;
+            if (f < 1) return 1;
+
+            return (int)(f+0.5f);
         }
 
+        // looks like we were unable to resolve the dimension value
+        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                String.format(
+                    "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+                    s, mNames[index]), null /*data*/);
+
         return defValue;
     }