Merge "Check camera permissions for any devices using the VIDEO class"

This commit is contained in:
TreeHugger Robot
2017-11-22 12:50:19 +00:00
committed by Android (Google) Code Review
5 changed files with 102 additions and 17 deletions

View File

@@ -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);

View File

@@ -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
*/

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;
}
public boolean hasPermission(UsbDevice device) {
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;
}
}
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);