USB: Add API and dialog for apps to request permissions for USB devices and accessories

New APIs:

UsbManager.hasPermission returns true if the caller has permission
for the given device or accessory

UsbManager.requestPermission poses a dialog to allow the user to give the caller
permission for the device or accessory.
Result is returned via a PendingIntent.
No dialog is displayed if the caller already has permission.

Also moved UsbResolverActivity to SystemUI package

BUG: 4069037

Change-Id: I93be769501a8776b49ac26e468af19f8fa2114c9
diff --git a/api/current.xml b/api/current.xml
index 4996e6a..3a24d17 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"
@@ -267304,7 +267371,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/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index be65bdb..c79a458 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);
 
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 6683179..9f1b8ea 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;
@@ -176,12 +178,24 @@
      */
     public static final String EXTRA_ACCESSORY = "accessory";
 
-    private IUsbManager mService;
+    /**
+     * Name of extra added to the {@link android.app.PendingIntent}
+     * passed into
+     * {#requestPermission(android.content.Context, android.hardware.usb.UsbDevice, android.app.PendingIntent)}
+     * or
+     * {#requestPermission(android.content.Context, android.hardware.usb.UsbAccessory, android.app.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 +259,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 +274,87 @@
         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.
+     *
+     * @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.
+     *
+     * @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 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.
+     * 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 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.
+     * 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/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/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/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/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/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
new file mode 100644
index 0000000..92c6d3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -0,0 +1,164 @@
+/*
+ * 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;
+
+    @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);
+        } else {
+            ap.mMessage = getString(R.string.usb_device_permission_prompt, appName);
+        }
+        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);
+        }
+
+        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 97%
rename from services/java/com/android/server/usb/UsbResolverActivity.java
rename to packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index e8a09a5..1d7f70f 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;
 
@@ -50,7 +50,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. */
diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
index e66f685..29e6f94 100644
--- a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usb;
 
+import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -691,17 +692,21 @@
                 Log.e(TAG, "startActivity failed", e);
             }
         } else {
-            // start UsbResolverActivity so user can choose an activity
-            Intent resolverIntent = new Intent(mContext, UsbResolverActivity.class);
-            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            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(UsbResolverActivity.EXTRA_RESOLVE_INFOS,
-                    matches);
+            resolverIntent.putParcelableArrayListExtra("rlist", matches);
             try {
                 mContext.startActivity(resolverIntent);
             } catch (ActivityNotFoundException e) {
                 Log.e(TAG, "unable to start UsbResolverActivity");
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
     }
@@ -713,40 +718,121 @@
         mContext.sendBroadcast(intent);
     }
 
-    public void checkPermission(UsbDevice device) {
-        if (device == null) return;
+    public boolean hasPermission(UsbDevice device) {
         synchronized (mLock) {
-            ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(Binder.getCallingUid());
+            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;
+                        return true;
                     }
                 }
             }
         }
-        throw new SecurityException("User has not given permission to device " + device);
+        return false;
     }
 
-    public void checkPermission(UsbAccessory accessory) {
-        if (accessory == null) return;
+    public boolean hasPermission(UsbAccessory accessory) {
         synchronized (mLock) {
-            ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid());
+            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;
+                        return true;
                     }
                 }
             }
         }
-        throw new SecurityException("User has not given permission to accessory " + accessory);
+        return false;
+    }
+
+    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 (!hasPermission(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) {
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 8170c61..d0a2492 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);