1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.usb; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.PendingIntent; 22 import android.content.ActivityNotFoundException; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.hardware.SensorPrivacyManager.Sensors; 29 import android.hardware.SensorPrivacyManagerInternal; 30 import android.hardware.usb.AccessoryFilter; 31 import android.hardware.usb.DeviceFilter; 32 import android.hardware.usb.UsbAccessory; 33 import android.hardware.usb.UsbDevice; 34 import android.hardware.usb.UsbManager; 35 import android.os.AsyncTask; 36 import android.os.Binder; 37 import android.os.Environment; 38 import android.os.Process; 39 import android.os.UserHandle; 40 import android.service.usb.UsbAccessoryPermissionProto; 41 import android.service.usb.UsbAccessoryPersistentPermissionProto; 42 import android.service.usb.UsbDevicePermissionProto; 43 import android.service.usb.UsbDevicePersistentPermissionProto; 44 import android.service.usb.UsbUidPermissionProto; 45 import android.service.usb.UsbUserPermissionsManagerProto; 46 import android.util.ArrayMap; 47 import android.util.AtomicFile; 48 import android.util.EventLog; 49 import android.util.Slog; 50 import android.util.SparseBooleanArray; 51 import android.util.Xml; 52 53 import com.android.internal.annotations.GuardedBy; 54 import com.android.internal.util.XmlUtils; 55 import com.android.internal.util.dump.DualDumpOutputStream; 56 import com.android.modules.utils.TypedXmlPullParser; 57 import com.android.modules.utils.TypedXmlSerializer; 58 import com.android.server.LocalServices; 59 60 import org.xmlpull.v1.XmlPullParser; 61 import org.xmlpull.v1.XmlPullParserException; 62 63 import java.io.File; 64 import java.io.FileInputStream; 65 import java.io.FileNotFoundException; 66 import java.io.FileOutputStream; 67 import java.io.IOException; 68 69 /** 70 * UsbUserPermissionManager manages usb device or accessory access permissions. 71 * 72 * @hide 73 */ 74 class UsbUserPermissionManager { 75 private static final String TAG = UsbUserPermissionManager.class.getSimpleName(); 76 private static final boolean DEBUG = false; 77 78 private static final int SNET_EVENT_LOG_ID = 0x534e4554; 79 80 @GuardedBy("mLock") 81 /** Mapping of USB device name to list of UIDs with permissions for the device 82 * Each entry lasts until device is disconnected*/ 83 private final ArrayMap<String, SparseBooleanArray> mDevicePermissionMap = 84 new ArrayMap<>(); 85 @GuardedBy("mLock") 86 /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory 87 * Each entry lasts until accessory is disconnected*/ 88 private final ArrayMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap = 89 new ArrayMap<>(); 90 91 @GuardedBy("mLock") 92 /** Maps USB device to list of UIDs with persistent permissions for the device*/ 93 private final ArrayMap<DeviceFilter, SparseBooleanArray> 94 mDevicePersistentPermissionMap = new ArrayMap<>(); 95 @GuardedBy("mLock") 96 /** Maps Usb Accessory to list of UIDs with persistent permissions for the accessory*/ 97 private final ArrayMap<AccessoryFilter, SparseBooleanArray> 98 mAccessoryPersistentPermissionMap = new ArrayMap<>(); 99 100 private final Context mContext; 101 private final UserHandle mUser; 102 private final UsbUserSettingsManager mUsbUserSettingsManager; 103 private final boolean mDisablePermissionDialogs; 104 105 private final @NonNull AtomicFile mPermissionsFile; 106 107 private final Object mLock = new Object(); 108 109 /** 110 * If a async task to persist the mDevicePersistentPreferenceMap and 111 * mAccessoryPersistentPreferenceMap is currently scheduled. 112 */ 113 @GuardedBy("mLock") 114 private boolean mIsCopyPermissionsScheduled; 115 private final SensorPrivacyManagerInternal mSensorPrivacyMgrInternal; 116 UsbUserPermissionManager(@onNull Context context, @NonNull UsbUserSettingsManager usbUserSettingsManager)117 UsbUserPermissionManager(@NonNull Context context, 118 @NonNull UsbUserSettingsManager usbUserSettingsManager) { 119 mContext = context; 120 mUser = context.getUser(); 121 mUsbUserSettingsManager = usbUserSettingsManager; 122 mSensorPrivacyMgrInternal = LocalServices.getService(SensorPrivacyManagerInternal.class); 123 mDisablePermissionDialogs = context.getResources().getBoolean( 124 com.android.internal.R.bool.config_disableUsbPermissionDialogs); 125 126 mPermissionsFile = new AtomicFile(new File( 127 Environment.getUserSystemDirectory(mUser.getIdentifier()), 128 "usb_permissions.xml"), "usb-permissions"); 129 synchronized (mLock) { 130 readPermissionsLocked(); 131 } 132 } 133 134 /** 135 * Removes access permissions of all packages for the USB accessory. 136 * 137 * @param accessory to remove permissions for 138 */ removeAccessoryPermissions(@onNull UsbAccessory accessory)139 void removeAccessoryPermissions(@NonNull UsbAccessory accessory) { 140 synchronized (mLock) { 141 mAccessoryPermissionMap.remove(accessory); 142 } 143 } 144 145 /** 146 * Removes access permissions of all packages for the USB device. 147 * 148 * @param device to remove permissions for 149 */ removeDevicePermissions(@onNull UsbDevice device)150 void removeDevicePermissions(@NonNull UsbDevice device) { 151 synchronized (mLock) { 152 mDevicePermissionMap.remove(device.getDeviceName()); 153 } 154 } 155 156 /** 157 * Grants permission for USB device without showing system dialog for package with uid. 158 * 159 * @param device to grant permission for 160 * @param uid to grant permission for 161 */ grantDevicePermission(@onNull UsbDevice device, int uid)162 void grantDevicePermission(@NonNull UsbDevice device, int uid) { 163 synchronized (mLock) { 164 String deviceName = device.getDeviceName(); 165 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); 166 if (uidList == null) { 167 uidList = new SparseBooleanArray(1); 168 mDevicePermissionMap.put(deviceName, uidList); 169 } 170 uidList.put(uid, true); 171 } 172 } 173 174 /** 175 * Grants permission for USB accessory without showing system dialog for package with uid. 176 * 177 * @param accessory to grant permission for 178 * @param uid to grant permission for 179 */ grantAccessoryPermission(@onNull UsbAccessory accessory, int uid)180 void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) { 181 synchronized (mLock) { 182 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 183 if (uidList == null) { 184 uidList = new SparseBooleanArray(1); 185 mAccessoryPermissionMap.put(accessory, uidList); 186 } 187 uidList.put(uid, true); 188 } 189 } 190 191 /** 192 * Returns true if package with uid has permission to access the device. 193 * 194 * @param device to check permission for 195 * @param pid to check permission for 196 * @param uid to check permission for 197 * @return {@code true} if package with uid has permission 198 */ hasPermission(@onNull UsbDevice device, @NonNull String packageName, int pid, int uid)199 boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid, 200 int uid) { 201 if (device.getHasVideoCapture()) { 202 boolean isCameraPrivacyEnabled = mSensorPrivacyMgrInternal.isSensorPrivacyEnabled( 203 UserHandle.getUserId(uid), Sensors.CAMERA); 204 if (DEBUG) { 205 Slog.d(TAG, "isCameraPrivacyEnabled: " + isCameraPrivacyEnabled); 206 } 207 if (isCameraPrivacyEnabled || !isCameraPermissionGranted(packageName, pid, uid)) { 208 return false; 209 } 210 } 211 // Only check for microphone privacy and not RECORD_AUDIO permission, because access to usb 212 // camera device with audio recording capabilities may still be granted with a warning 213 if (device.getHasAudioCapture() && mSensorPrivacyMgrInternal.isSensorPrivacyEnabled( 214 UserHandle.getUserId(uid), Sensors.MICROPHONE)) { 215 if (DEBUG) { 216 Slog.d(TAG, 217 "Access to device with audio recording capabilities denied because " 218 + "microphone privacy is enabled."); 219 } 220 return false; 221 } 222 synchronized (mLock) { 223 if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { 224 return true; 225 } 226 DeviceFilter filter = new DeviceFilter(device); 227 SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter); 228 if (permissionsForDevice != null) { 229 int idx = permissionsForDevice.indexOfKey(uid); 230 if (idx >= 0) { 231 return permissionsForDevice.valueAt(idx); 232 } 233 } 234 SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); 235 if (uidList == null) { 236 return false; 237 } 238 return uidList.get(uid); 239 } 240 } 241 242 /** 243 * Returns true if caller has permission to access the accessory. 244 * 245 * @param accessory to check permission for 246 * @param uid to check permission for 247 * @return {@code true} if caller has permssion 248 */ hasPermission(@onNull UsbAccessory accessory, int pid, int uid)249 boolean hasPermission(@NonNull UsbAccessory accessory, int pid, int uid) { 250 synchronized (mLock) { 251 if (uid == Process.SYSTEM_UID 252 || mDisablePermissionDialogs 253 || mContext.checkPermission( 254 android.Manifest.permission.MANAGE_USB, pid, uid) 255 == android.content.pm.PackageManager.PERMISSION_GRANTED) { 256 return true; 257 } 258 AccessoryFilter filter = new AccessoryFilter(accessory); 259 SparseBooleanArray permissionsForAccessory = 260 mAccessoryPersistentPermissionMap.get(filter); 261 if (permissionsForAccessory != null) { 262 int idx = permissionsForAccessory.indexOfKey(uid); 263 if (idx >= 0) { 264 return permissionsForAccessory.valueAt(idx); 265 } 266 } 267 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 268 if (uidList == null) { 269 return false; 270 } 271 return uidList.get(uid); 272 } 273 } 274 setDevicePersistentPermission(@onNull UsbDevice device, int uid, boolean isGranted)275 void setDevicePersistentPermission(@NonNull UsbDevice device, int uid, boolean isGranted) { 276 277 boolean isChanged; 278 DeviceFilter filter = new DeviceFilter(device); 279 synchronized (mLock) { 280 SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter); 281 if (permissionsForDevice == null) { 282 permissionsForDevice = new SparseBooleanArray(); 283 mDevicePersistentPermissionMap.put(filter, permissionsForDevice); 284 } 285 int idx = permissionsForDevice.indexOfKey(uid); 286 if (idx >= 0) { 287 isChanged = permissionsForDevice.valueAt(idx) != isGranted; 288 permissionsForDevice.setValueAt(idx, isGranted); 289 } else { 290 isChanged = true; 291 permissionsForDevice.put(uid, isGranted); 292 } 293 294 if (isChanged) { 295 scheduleWritePermissionsLocked(); 296 } 297 } 298 } 299 setAccessoryPersistentPermission(@onNull UsbAccessory accessory, int uid, boolean isGranted)300 void setAccessoryPersistentPermission(@NonNull UsbAccessory accessory, int uid, 301 boolean isGranted) { 302 303 boolean isChanged; 304 AccessoryFilter filter = new AccessoryFilter(accessory); 305 synchronized (mLock) { 306 SparseBooleanArray permissionsForAccessory = 307 mAccessoryPersistentPermissionMap.get(filter); 308 if (permissionsForAccessory == null) { 309 permissionsForAccessory = new SparseBooleanArray(); 310 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory); 311 } 312 int idx = permissionsForAccessory.indexOfKey(uid); 313 if (idx >= 0) { 314 isChanged = permissionsForAccessory.valueAt(idx) != isGranted; 315 permissionsForAccessory.setValueAt(idx, isGranted); 316 } else { 317 isChanged = true; 318 permissionsForAccessory.put(uid, isGranted); 319 } 320 321 if (isChanged) { 322 scheduleWritePermissionsLocked(); 323 } 324 } 325 } 326 readPermission(@onNull XmlPullParser parser)327 private void readPermission(@NonNull XmlPullParser parser) throws XmlPullParserException, 328 IOException { 329 int uid; 330 boolean isGranted; 331 332 try { 333 uid = XmlUtils.readIntAttribute(parser, "uid"); 334 } catch (NumberFormatException e) { 335 Slog.e(TAG, "error reading usb permission uid", e); 336 XmlUtils.skipCurrentTag(parser); 337 return; 338 } 339 340 // only use "true"/"false" as valid values 341 String isGrantedString = parser.getAttributeValue(null, "granted"); 342 if (isGrantedString == null || !(isGrantedString.equals(Boolean.TRUE.toString()) 343 || isGrantedString.equals(Boolean.FALSE.toString()))) { 344 Slog.e(TAG, "error reading usb permission granted state"); 345 XmlUtils.skipCurrentTag(parser); 346 return; 347 } 348 isGranted = isGrantedString.equals(Boolean.TRUE.toString()); 349 XmlUtils.nextElement(parser); 350 if ("usb-device".equals(parser.getName())) { 351 DeviceFilter filter = DeviceFilter.read(parser); 352 int idx = mDevicePersistentPermissionMap.indexOfKey(filter); 353 if (idx >= 0) { 354 SparseBooleanArray permissionsForDevice = 355 mDevicePersistentPermissionMap.valueAt(idx); 356 permissionsForDevice.put(uid, isGranted); 357 } else { 358 SparseBooleanArray permissionsForDevice = new SparseBooleanArray(); 359 mDevicePersistentPermissionMap.put(filter, permissionsForDevice); 360 permissionsForDevice.put(uid, isGranted); 361 } 362 } else if ("usb-accessory".equals(parser.getName())) { 363 AccessoryFilter filter = AccessoryFilter.read(parser); 364 int idx = mAccessoryPersistentPermissionMap.indexOfKey(filter); 365 if (idx >= 0) { 366 SparseBooleanArray permissionsForAccessory = 367 mAccessoryPersistentPermissionMap.valueAt(idx); 368 permissionsForAccessory.put(uid, isGranted); 369 } else { 370 SparseBooleanArray permissionsForAccessory = new SparseBooleanArray(); 371 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory); 372 permissionsForAccessory.put(uid, isGranted); 373 } 374 } 375 } 376 377 @GuardedBy("mLock") readPermissionsLocked()378 private void readPermissionsLocked() { 379 mDevicePersistentPermissionMap.clear(); 380 mAccessoryPersistentPermissionMap.clear(); 381 382 try (FileInputStream in = mPermissionsFile.openRead()) { 383 TypedXmlPullParser parser = Xml.resolvePullParser(in); 384 385 XmlUtils.nextElement(parser); 386 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 387 String tagName = parser.getName(); 388 if ("permission".equals(tagName)) { 389 readPermission(parser); 390 } else { 391 XmlUtils.nextElement(parser); 392 } 393 } 394 } catch (FileNotFoundException e) { 395 if (DEBUG) Slog.d(TAG, "usb permissions file not found"); 396 } catch (Exception e) { 397 Slog.e(TAG, "error reading usb permissions file, deleting to start fresh", e); 398 mPermissionsFile.delete(); 399 } 400 } 401 402 @GuardedBy("mLock") scheduleWritePermissionsLocked()403 private void scheduleWritePermissionsLocked() { 404 if (mIsCopyPermissionsScheduled) { 405 return; 406 } 407 mIsCopyPermissionsScheduled = true; 408 409 AsyncTask.execute(() -> { 410 int numDevices; 411 DeviceFilter[] devices; 412 int[][] uidsForDevices; 413 boolean[][] grantedValuesForDevices; 414 415 int numAccessories; 416 AccessoryFilter[] accessories; 417 int[][] uidsForAccessories; 418 boolean[][] grantedValuesForAccessories; 419 420 synchronized (mLock) { 421 // Copy the permission state so we can write outside of lock 422 numDevices = mDevicePersistentPermissionMap.size(); 423 devices = new DeviceFilter[numDevices]; 424 uidsForDevices = new int[numDevices][]; 425 grantedValuesForDevices = new boolean[numDevices][]; 426 for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) { 427 devices[deviceIdx] = 428 new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx)); 429 SparseBooleanArray permissions = 430 mDevicePersistentPermissionMap.valueAt(deviceIdx); 431 int numPermissions = permissions.size(); 432 uidsForDevices[deviceIdx] = new int[numPermissions]; 433 grantedValuesForDevices[deviceIdx] = new boolean[numPermissions]; 434 for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) { 435 uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx); 436 grantedValuesForDevices[deviceIdx][permissionIdx] = 437 permissions.valueAt(permissionIdx); 438 } 439 } 440 441 numAccessories = mAccessoryPersistentPermissionMap.size(); 442 accessories = new AccessoryFilter[numAccessories]; 443 uidsForAccessories = new int[numAccessories][]; 444 grantedValuesForAccessories = new boolean[numAccessories][]; 445 for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) { 446 accessories[accessoryIdx] = new AccessoryFilter( 447 mAccessoryPersistentPermissionMap.keyAt(accessoryIdx)); 448 SparseBooleanArray permissions = 449 mAccessoryPersistentPermissionMap.valueAt(accessoryIdx); 450 int numPermissions = permissions.size(); 451 uidsForAccessories[accessoryIdx] = new int[numPermissions]; 452 grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions]; 453 for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) { 454 uidsForAccessories[accessoryIdx][permissionIdx] = 455 permissions.keyAt(permissionIdx); 456 grantedValuesForAccessories[accessoryIdx][permissionIdx] = 457 permissions.valueAt(permissionIdx); 458 } 459 } 460 mIsCopyPermissionsScheduled = false; 461 } 462 463 synchronized (mPermissionsFile) { 464 FileOutputStream out = null; 465 try { 466 out = mPermissionsFile.startWrite(); 467 TypedXmlSerializer serializer = Xml.resolveSerializer(out); 468 serializer.startDocument(null, true); 469 serializer.startTag(null, "permissions"); 470 471 for (int i = 0; i < numDevices; i++) { 472 int numPermissions = uidsForDevices[i].length; 473 for (int j = 0; j < numPermissions; j++) { 474 serializer.startTag(null, "permission"); 475 serializer.attribute(null, "uid", 476 Integer.toString(uidsForDevices[i][j])); 477 serializer.attribute(null, "granted", 478 Boolean.toString(grantedValuesForDevices[i][j])); 479 devices[i].write(serializer); 480 serializer.endTag(null, "permission"); 481 } 482 } 483 484 for (int i = 0; i < numAccessories; i++) { 485 int numPermissions = uidsForAccessories[i].length; 486 for (int j = 0; j < numPermissions; j++) { 487 serializer.startTag(null, "permission"); 488 serializer.attribute(null, "uid", 489 Integer.toString(uidsForAccessories[i][j])); 490 serializer.attribute(null, "granted", 491 Boolean.toString(grantedValuesForDevices[i][j])); 492 accessories[i].write(serializer); 493 serializer.endTag(null, "permission"); 494 } 495 } 496 497 serializer.endTag(null, "permissions"); 498 serializer.endDocument(); 499 500 mPermissionsFile.finishWrite(out); 501 } catch (IOException e) { 502 Slog.e(TAG, "Failed to write permissions", e); 503 if (out != null) { 504 mPermissionsFile.failWrite(out); 505 } 506 } 507 } 508 }); 509 } 510 511 /** 512 * Creates UI dialog to request permission for the given package to access the device 513 * or accessory. 514 * 515 * @param device The USB device attached 516 * @param accessory The USB accessory attached 517 * @param canBeDefault Whether the calling pacakge can set as default handler 518 * of the USB device or accessory 519 * @param packageName The package name of the calling package 520 * @param uid The uid of the calling package 521 * @param userContext The context to start the UI dialog 522 * @param pi PendingIntent for returning result 523 */ requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, @NonNull String packageName, int uid, @NonNull Context userContext, @NonNull PendingIntent pi)524 void requestPermissionDialog(@Nullable UsbDevice device, 525 @Nullable UsbAccessory accessory, 526 boolean canBeDefault, 527 @NonNull String packageName, 528 int uid, 529 @NonNull Context userContext, 530 @NonNull PendingIntent pi) { 531 final long identity = Binder.clearCallingIdentity(); 532 try { 533 Intent intent = new Intent(); 534 if (device != null) { 535 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 536 } else { 537 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 538 } 539 intent.putExtra(Intent.EXTRA_INTENT, pi); 540 intent.putExtra(Intent.EXTRA_UID, uid); 541 intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault); 542 intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName); 543 intent.setComponent( 544 ComponentName.unflattenFromString(userContext.getResources().getString( 545 com.android.internal.R.string.config_usbPermissionActivity))); 546 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 547 548 userContext.startActivityAsUser(intent, mUser); 549 } catch (ActivityNotFoundException e) { 550 Slog.e(TAG, "unable to start UsbPermissionActivity"); 551 } finally { 552 Binder.restoreCallingIdentity(identity); 553 } 554 } 555 dump(@onNull DualDumpOutputStream dump, String idName, long id)556 void dump(@NonNull DualDumpOutputStream dump, String idName, long id) { 557 long token = dump.start(idName, id); 558 synchronized (mLock) { 559 dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier()); 560 int numMappings = mDevicePermissionMap.size(); 561 for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) { 562 String deviceName = mDevicePermissionMap.keyAt(mappingsIdx); 563 long devicePermissionToken = dump.start("device_permissions", 564 UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS); 565 566 dump.write("device_name", UsbDevicePermissionProto.DEVICE_NAME, deviceName); 567 568 SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx); 569 int numUids = uidList.size(); 570 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) { 571 dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx)); 572 } 573 574 dump.end(devicePermissionToken); 575 } 576 577 numMappings = mAccessoryPermissionMap.size(); 578 for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) { 579 UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx); 580 long accessoryPermissionToken = dump.start("accessory_permissions", 581 UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS); 582 583 dump.write("accessory_description", 584 UsbAccessoryPermissionProto.ACCESSORY_DESCRIPTION, 585 accessory.getDescription()); 586 587 SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx); 588 int numUids = uidList.size(); 589 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) { 590 dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx)); 591 } 592 593 dump.end(accessoryPermissionToken); 594 } 595 596 numMappings = mDevicePersistentPermissionMap.size(); 597 for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) { 598 DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx); 599 long devicePermissionToken = dump.start("device_persistent_permissions", 600 UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS); 601 filter.dump(dump, "device", 602 UsbDevicePersistentPermissionProto.DEVICE_FILTER); 603 SparseBooleanArray permissions = 604 mDevicePersistentPermissionMap.valueAt(mappingsIdx); 605 int numPermissions = permissions.size(); 606 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) { 607 long uidPermissionToken = dump.start("uid_permission", 608 UsbDevicePersistentPermissionProto.PERMISSION_VALUES); 609 dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx)); 610 dump.write("is_granted", 611 UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx)); 612 dump.end(uidPermissionToken); 613 } 614 dump.end(devicePermissionToken); 615 } 616 617 numMappings = mAccessoryPersistentPermissionMap.size(); 618 for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) { 619 AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx); 620 long accessoryPermissionToken = dump.start("accessory_persistent_permissions", 621 UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS); 622 filter.dump(dump, "accessory", 623 UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER); 624 SparseBooleanArray permissions = 625 mAccessoryPersistentPermissionMap.valueAt(mappingsIdx); 626 int numPermissions = permissions.size(); 627 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) { 628 long uidPermissionToken = dump.start("uid_permission", 629 UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES); 630 dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx)); 631 dump.write("is_granted", 632 UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx)); 633 dump.end(uidPermissionToken); 634 } 635 dump.end(accessoryPermissionToken); 636 } 637 } 638 dump.end(token); 639 } 640 641 /** 642 * Check for camera permission of the calling process. 643 * 644 * @param packageName Package name of the caller. 645 * @param pid Linux pid of the calling process. 646 * @param uid Linux uid of the calling process. 647 * @return True in case camera permission is available, False otherwise. 648 */ isCameraPermissionGranted(String packageName, int pid, int uid)649 private boolean isCameraPermissionGranted(String packageName, int pid, int uid) { 650 int targetSdkVersion = android.os.Build.VERSION_CODES.P; 651 try { 652 ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0); 653 // compare uid with packageName to foil apps pretending to be someone else 654 if (aInfo.uid != uid) { 655 Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid); 656 return false; 657 } 658 targetSdkVersion = aInfo.targetSdkVersion; 659 } catch (PackageManager.NameNotFoundException e) { 660 Slog.i(TAG, "Package not found, likely due to invalid package name!"); 661 return false; 662 } 663 664 if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) { 665 int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid); 666 if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) { 667 Slog.i(TAG, "Camera permission required for USB video class devices"); 668 return false; 669 } 670 } 671 672 return true; 673 } 674 checkPermission(UsbDevice device, String packageName, int pid, int uid)675 public void checkPermission(UsbDevice device, String packageName, int pid, int uid) { 676 if (!hasPermission(device, packageName, pid, uid)) { 677 throw new SecurityException("User has not given " + uid + "/" + packageName 678 + " permission to access device " + device.getDeviceName()); 679 } 680 } 681 checkPermission(UsbAccessory accessory, int pid, int uid)682 public void checkPermission(UsbAccessory accessory, int pid, int uid) { 683 if (!hasPermission(accessory, pid, uid)) { 684 throw new SecurityException("User has not given " + uid + " permission to accessory " 685 + accessory); 686 } 687 } 688 requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, String packageName, PendingIntent pi, int uid)689 private void requestPermissionDialog(@Nullable UsbDevice device, 690 @Nullable UsbAccessory accessory, 691 boolean canBeDefault, 692 String packageName, 693 PendingIntent pi, 694 int uid) { 695 boolean throwException = false; 696 697 // compare uid with packageName to foil apps pretending to be someone else 698 try { 699 ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0); 700 if (aInfo.uid != uid) { 701 Slog.w(TAG, "package " + packageName 702 + " does not match caller's uid " + uid); 703 EventLog.writeEvent(SNET_EVENT_LOG_ID, "180104273", -1, ""); 704 throwException = true; 705 } 706 } catch (PackageManager.NameNotFoundException e) { 707 throwException = true; 708 } finally { 709 if (throwException) 710 throw new IllegalArgumentException("package " + packageName + " not found"); 711 } 712 713 requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi); 714 } 715 requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid, int uid)716 public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid, 717 int uid) { 718 Intent intent = new Intent(); 719 720 // respond immediately if permission has already been granted 721 if (hasPermission(device, packageName, pid, uid)) { 722 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 723 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 724 try { 725 pi.send(mContext, 0, intent); 726 } catch (PendingIntent.CanceledException e) { 727 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 728 } 729 return; 730 } 731 // If the app doesn't have camera permission do not request permission to the USB device. 732 // Note that if the USB camera also has a microphone, a warning will be shown to the user if 733 // the app doesn't have RECORD_AUDIO permission. 734 if (device.getHasVideoCapture()) { 735 if (!isCameraPermissionGranted(packageName, pid, uid)) { 736 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 737 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); 738 try { 739 pi.send(mContext, 0, intent); 740 } catch (PendingIntent.CanceledException e) { 741 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 742 } 743 return; 744 } 745 } 746 747 requestPermissionDialog(device, null, 748 mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid); 749 } 750 requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi, int pid, int uid)751 public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi, 752 int pid, int uid) { 753 // respond immediately if permission has already been granted 754 if (hasPermission(accessory, pid, uid)) { 755 Intent intent = new Intent(); 756 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 757 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 758 try { 759 pi.send(mContext, 0, intent); 760 } catch (PendingIntent.CanceledException e) { 761 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 762 } 763 return; 764 } 765 766 requestPermissionDialog(null, accessory, 767 mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid); 768 } 769 } 770