diff options
-rw-r--r-- | api/current.xml | 69 | ||||
-rw-r--r-- | core/java/android/app/ContextImpl.java | 6 | ||||
-rw-r--r-- | core/java/android/hardware/usb/IUsbManager.aidl | 20 | ||||
-rw-r--r-- | core/java/android/hardware/usb/UsbManager.java | 98 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 6 | ||||
-rw-r--r-- | media/java/android/mtp/MtpClient.java | 57 | ||||
-rw-r--r-- | packages/SystemUI/AndroidManifest.xml | 18 | ||||
-rw-r--r-- | packages/SystemUI/res/values/strings.xml | 7 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java | 164 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java (renamed from services/java/com/android/server/usb/UsbResolverActivity.java) | 3 | ||||
-rw-r--r-- | services/java/com/android/server/usb/UsbDeviceSettingsManager.java | 114 | ||||
-rw-r--r-- | services/java/com/android/server/usb/UsbService.java | 19 |
12 files changed, 534 insertions, 47 deletions
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=""permission"" + 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 @@ class ContextImpl extends Context { 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 @@ interface IUsbManager */ 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 class UsbManager { */ 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 @@ public class UsbManager { 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 @@ public class UsbManager { 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 @@ public class MtpClient { 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 @@ public class MtpClient { // 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 class MtpClient { 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 @@ public class MtpClient { */ 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 @@ public class MtpClient { // 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 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 @@ public class UsbResolverActivity extends ResolverActivity { } 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 @@ class UsbDeviceSettingsManager { Log.e(TAG, "startActivity failed", e); } } else { + long identity = Binder.clearCallingIdentity(); + // start UsbResolverActivity so user can choose an activity - Intent resolverIntent = new Intent(mContext, UsbResolverActivity.class); + 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 @@ class UsbDeviceSettingsManager { 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 @@ public class UsbService extends IUsbManager.Stub { 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); |