diff options
5 files changed, 102 insertions, 17 deletions
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 025d46d12567..151e62de7b70 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -34,7 +34,7 @@ interface IUsbManager /* Returns a file descriptor for communicating with the USB device. * The native fd can be passed to usb_device_new() in libusbhost. */ - ParcelFileDescriptor openDevice(String deviceName); + ParcelFileDescriptor openDevice(String deviceName, String packageName); /* Returns the currently attached USB accessory */ UsbAccessory getCurrentAccessory(); @@ -55,7 +55,7 @@ interface IUsbManager void setAccessoryPackage(in UsbAccessory accessory, String packageName, int userId); /* Returns true if the caller has permission to access the device. */ - boolean hasDevicePermission(in UsbDevice device); + boolean hasDevicePermission(in UsbDevice device, String packageName); /* Returns true if the caller has permission to access the accessory. */ boolean hasAccessoryPermission(in UsbAccessory accessory); diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 6ce96698e444..bdb90bcca4f8 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -344,7 +344,7 @@ public class UsbManager { public UsbDeviceConnection openDevice(UsbDevice device) { try { String deviceName = device.getDeviceName(); - ParcelFileDescriptor pfd = mService.openDevice(deviceName); + ParcelFileDescriptor pfd = mService.openDevice(deviceName, mContext.getPackageName()); if (pfd != null) { UsbDeviceConnection connection = new UsbDeviceConnection(device); boolean result = connection.open(deviceName, pfd, mContext); @@ -400,6 +400,9 @@ public class UsbManager { * Permission might have been granted temporarily via * {@link #requestPermission(UsbDevice, PendingIntent)} or * by the user choosing the caller as the default application for the device. + * Permission for USB devices of class {@link UsbConstants#USB_CLASS_VIDEO} for clients that + * target SDK {@link android.os.Build.VERSION_CODES#P} and above can be granted only if they + * have additionally the {@link android.Manifest.permission#CAMERA} permission. * * @param device to check permissions for * @return true if caller has permission @@ -409,7 +412,7 @@ public class UsbManager { return false; } try { - return mService.hasDevicePermission(device); + return mService.hasDevicePermission(device, mContext.getPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -450,6 +453,10 @@ public class UsbManager { * permission was granted by the user * </ul> * + * Permission for USB devices of class {@link UsbConstants#USB_CLASS_VIDEO} for clients that + * target SDK {@link android.os.Build.VERSION_CODES#P} and above can be granted only if they + * have additionally the {@link android.Manifest.permission#CAMERA} permission. + * * @param device to request permissions for * @param pi PendingIntent for returning result */ diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 095fdc63975c..9bc9cd04957c 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -323,7 +323,8 @@ public class UsbHostManager { } /* Opens the specified USB device */ - public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings) { + public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings, + String packageName, int uid) { synchronized (mLock) { if (isBlackListed(deviceName)) { throw new SecurityException("USB device is on a restricted bus"); @@ -334,7 +335,7 @@ public class UsbHostManager { throw new IllegalArgumentException( "device " + deviceName + " does not exist or is restricted"); } - settings.checkPermission(device); + settings.checkPermission(device, packageName, uid); return nativeOpenDevice(deviceName); } } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index e4fcea77fa44..17de83f0cac9 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -232,7 +232,7 @@ public class UsbService extends IUsbManager.Stub { /* Opens the specified USB device (host mode) */ @Override - public ParcelFileDescriptor openDevice(String deviceName) { + public ParcelFileDescriptor openDevice(String deviceName, String packageName) { ParcelFileDescriptor fd = null; if (mHostManager != null) { @@ -242,7 +242,8 @@ public class UsbService extends IUsbManager.Stub { boolean isCurrentUser = isCallerInCurrentUserProfileGroupLocked(); if (isCurrentUser) { - fd = mHostManager.openDevice(deviceName, getSettingsForUser(userIdInt)); + fd = mHostManager.openDevice(deviceName, getSettingsForUser(userIdInt), + packageName, Binder.getCallingUid()); } else { Slog.w(TAG, "Cannot open " + deviceName + " for user " + userIdInt + " as user is not active."); @@ -308,9 +309,10 @@ public class UsbService extends IUsbManager.Stub { } @Override - public boolean hasDevicePermission(UsbDevice device) { + public boolean hasDevicePermission(UsbDevice device, String packageName) { final int userId = UserHandle.getCallingUserId(); - return getSettingsForUser(userId).hasPermission(device); + return getSettingsForUser(userId).hasPermission(device, packageName, + Binder.getCallingUid()); } @Override @@ -322,7 +324,8 @@ public class UsbService extends IUsbManager.Stub { @Override public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) { final int userId = UserHandle.getCallingUserId(); - getSettingsForUser(userId).requestPermission(device, packageName, pi); + getSettingsForUser(userId).requestPermission(device, packageName, pi, + Binder.getCallingUid()); } @Override diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java index 96c5211cecf4..11e43e308e8a 100644 --- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java @@ -26,6 +26,8 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbManager; import android.os.Binder; import android.os.Process; @@ -95,10 +97,70 @@ class UsbUserSettingsManager { } } + /** + * Check whether a particular device or any of its interfaces + * is of class VIDEO. + * + * @param device The device that needs to get scanned + * @return True in case a VIDEO device or interface is present, + * False otherwise. + */ + private boolean isCameraDevicePresent(UsbDevice device) { + if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) { + return true; + } + + for (int i = 0; i < device.getInterfaceCount(); i++) { + UsbInterface iface = device.getInterface(i); + if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) { + return true; + } + } + + return false; + } + + /** + * Check for camera permission of the calling process. + * + * @param packageName Package name of the caller. + * @param uid Linux uid of the calling process. + * + * @return True in case camera permission is available, False otherwise. + */ + private boolean isCameraPermissionGranted(String packageName, int uid) { + int targetSdkVersion = android.os.Build.VERSION_CODES.P; + try { + ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0); + // compare uid with packageName to foil apps pretending to be someone else + if (aInfo.uid != uid) { + Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid); + return false; + } + targetSdkVersion = aInfo.targetSdkVersion; + } catch (PackageManager.NameNotFoundException e) { + Slog.i(TAG, "Package not found, likely due to invalid package name!"); + return false; + } + + if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) { + int allowed = mUserContext.checkCallingPermission(android.Manifest.permission.CAMERA); + if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) { + Slog.i(TAG, "Camera permission required for USB video class devices"); + return false; + } + } - public boolean hasPermission(UsbDevice device) { + return true; + } + + public boolean hasPermission(UsbDevice device, String packageName, int uid) { synchronized (mLock) { - int uid = Binder.getCallingUid(); + if (isCameraDevicePresent(device)) { + if (!isCameraPermissionGranted(packageName, uid)) { + return false; + } + } if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { return true; } @@ -124,8 +186,8 @@ class UsbUserSettingsManager { } } - public void checkPermission(UsbDevice device) { - if (!hasPermission(device)) { + public void checkPermission(UsbDevice device, String packageName, int uid) { + if (!hasPermission(device, packageName, uid)) { throw new SecurityException("User has not given permission to device " + device); } } @@ -166,11 +228,11 @@ class UsbUserSettingsManager { } } - public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) { + public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) { Intent intent = new Intent(); // respond immediately if permission has already been granted - if (hasPermission(device)) { + if (hasPermission(device, packageName, uid)) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); try { @@ -180,6 +242,18 @@ class UsbUserSettingsManager { } return; } + if (isCameraDevicePresent(device)) { + if (!isCameraPermissionGranted(packageName, uid)) { + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); + try { + pi.send(mUserContext, 0, intent); + } catch (PendingIntent.CanceledException e) { + if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); + } + return; + } + } // start UsbPermissionActivity so user can choose an activity intent.putExtra(UsbManager.EXTRA_DEVICE, device); |