From 8c7d8c3ccb37edff424ca01c6474cbed2154d954 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 7 Mar 2011 16:33:21 -0500 Subject: [PATCH 1/3] UsbService: Automatically use system apps by default if it is the only choice If only one app is installed that supports a USB device or accessory and that app is in the system partition, then use that activity by default and rather than displaying the USB app chooser dialog. BUG: 4060064 Change-Id: I49bf22a439e9676039b6f612c9bb622ab426066c Signed-off-by: Mike Lockwood --- .../server/usb/UsbDeviceSettingsManager.java | 124 +++++++++--------- 1 file changed, 64 insertions(+), 60 deletions(-) diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java index 2f22fe1a5d7c2..e66f685fd35aa 100644 --- a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java +++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java @@ -21,6 +21,7 @@ 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; @@ -602,50 +603,20 @@ class UsbDeviceSettingsManager { } 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 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) { @@ -656,49 +627,82 @@ class UsbDeviceSettingsManager { } 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 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)); } + resolveActivity(intent, matches, defaultPackage, null, accessory); + } + + private void resolveActivity(Intent intent, ArrayList 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; - 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; + 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; + int uid = rInfo.activityInfo.applicationInfo.uid; + // grant permission + if (device != null) { + grantDevicePermission(device, uid); + } else if (accessory != null) { + grantAccessoryPermission(accessory, uid); } } } - Intent intent = new Intent(mContext, UsbResolverActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + 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; + } + } + } - 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"); + if (defaultRI != null) { + // 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 { + // start UsbResolverActivity so user can choose an activity + Intent resolverIntent = new Intent(mContext, UsbResolverActivity.class); + resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); + resolverIntent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, + matches); + try { + mContext.startActivity(resolverIntent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "unable to start UsbResolverActivity"); + } } } From 3a68b8338b431eb15d28e92f06452efbbda9493e Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 8 Mar 2011 10:08:59 -0500 Subject: [PATCH 2/3] 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 --- api/current.xml | 69 +++++++- core/java/android/app/ContextImpl.java | 6 +- .../android/hardware/usb/IUsbManager.aidl | 20 +++ .../java/android/hardware/usb/UsbManager.java | 98 ++++++++++- core/res/AndroidManifest.xml | 6 - media/java/android/mtp/MtpClient.java | 57 ++++-- packages/SystemUI/AndroidManifest.xml | 18 ++ packages/SystemUI/res/values/strings.xml | 7 + .../systemui/usb/UsbPermissionActivity.java | 164 ++++++++++++++++++ .../systemui}/usb/UsbResolverActivity.java | 3 +- .../server/usb/UsbDeviceSettingsManager.java | 116 +++++++++++-- .../com/android/server/usb/UsbService.java | 19 ++ 12 files changed, 535 insertions(+), 48 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java rename {services/java/com/android/server => packages/SystemUI/src/com/android/systemui}/usb/UsbResolverActivity.java (98%) diff --git a/api/current.xml b/api/current.xml index 4996e6aeb6de7..3a24d178bc0e7 100644 --- a/api/current.xml +++ b/api/current.xml @@ -95457,6 +95457,32 @@ visibility="public" > + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 539e946010490..cc1f81c5ec007 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 be65bdb403895..c79a4583f8a89 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 668317909a7f9..9f1b8eaab2d7f 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: + *
    + *
  • {@link #EXTRA_DEVICE} containing the device passed into this call + *
  • {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether + * permission was granted by the user + *
+ * + * @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: + *
    + *
  • {@link #EXTRA_ACCESSORY} containing the accessory passed into this call + *
  • {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether + * permission was granted by the user + *
+ * + * @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 0ad174fbf38fd..c684e7e7b8719 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1379,12 +1379,6 @@ android:excludeFromRecents="true"> - - - diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java index 40e2f9beed360..d25dcb9f5a68f 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 mListeners = new ArrayList(); @@ -49,29 +53,47 @@ public class MtpClient { // mDevices is also used for synchronization in this class. private final HashMap mDevices = new HashMap(); + 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 d3d175089680f..fee245f4b456f 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -8,6 +8,7 @@ + + + + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ebd48e7b68af4..becad6aa24f0f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -115,4 +115,11 @@ Use physical keyboard + + + Allow the application %1$s to access the USB device? + + + Allow the application %1$s to access the USB accessory? + 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 0000000000000..92c6d3db5e973 --- /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 98% rename from services/java/com/android/server/usb/UsbResolverActivity.java rename to packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java index e8a09a5cca719..1d7f70f79d06c 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 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 e66f685fd35aa..29e6f94d59710 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 { - // 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 @@ class UsbDeviceSettingsManager { mContext.sendBroadcast(intent); } - public void checkPermission(UsbDevice device) { - if (device == null) return; + public boolean hasPermission(UsbDevice device) { synchronized (mLock) { - ArrayList filterList = mDevicePermissionMap.get(Binder.getCallingUid()); + ArrayList 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 filterList = mAccessoryPermissionMap.get(Binder.getCallingUid()); + ArrayList 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 8170c61b5ec52..d0a24920943b4 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); From d591357524091254483849e37697255cc8fce2ad Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 8 Mar 2011 22:47:08 -0500 Subject: [PATCH 3/3] Close USB dialogs if their corresponding accessory or device has disconnected BUG: 4060065 Change-Id: Ib517e5e4a5422f9f0c8fca601308cecf7743c5d1 Signed-off-by: Mike Lockwood --- .../systemui/usb/UsbDisconnectedReceiver.java | 67 +++++++++++++++++++ .../systemui/usb/UsbPermissionActivity.java | 6 ++ .../systemui/usb/UsbResolverActivity.java | 45 +++++++++---- 3 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java 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 0000000000000..1edebbbacd798 --- /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 index 92c6d3db5e973..f1784df0e425f 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -57,6 +57,7 @@ public class UsbPermissionActivity extends AlertActivity private String mPackageName; private int mUid; private boolean mPermissionGranted; + private UsbDisconnectedReceiver mDisconnectedReceiver; @Override public void onCreate(Bundle icicle) { @@ -85,8 +86,10 @@ public class UsbPermissionActivity extends AlertActivity 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); @@ -142,6 +145,9 @@ public class UsbPermissionActivity extends AlertActivity Log.e(TAG, "IUsbService connection failed", e); } + if (mDisconnectedReceiver != null) { + unregisterReceiver(mDisconnectedReceiver); + } super.onDestroy(); } diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java index 1d7f70f79d06c..84d73dd9301d3 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java @@ -39,6 +39,10 @@ public class UsbResolverActivity extends ResolverActivity { 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(); @@ -57,6 +61,27 @@ public class UsbResolverActivity extends ResolverActivity { 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) { @@ -64,28 +89,24 @@ public class UsbResolverActivity extends ResolverActivity { 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); } }