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.TypedXmlPullParser; 52 import android.util.TypedXmlSerializer; 53 import android.util.Xml; 54 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.util.XmlUtils; 57 import com.android.internal.util.dump.DualDumpOutputStream; 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 uid)249 boolean hasPermission(@NonNull UsbAccessory accessory, int uid) { 250 synchronized (mLock) { 251 if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { 252 return true; 253 } 254 AccessoryFilter filter = new AccessoryFilter(accessory); 255 SparseBooleanArray permissionsForAccessory = 256 mAccessoryPersistentPermissionMap.get(filter); 257 if (permissionsForAccessory != null) { 258 int idx = permissionsForAccessory.indexOfKey(uid); 259 if (idx >= 0) { 260 return permissionsForAccessory.valueAt(idx); 261 } 262 } 263 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 264 if (uidList == null) { 265 return false; 266 } 267 return uidList.get(uid); 268 } 269 } 270 setDevicePersistentPermission(@onNull UsbDevice device, int uid, boolean isGranted)271 void setDevicePersistentPermission(@NonNull UsbDevice device, int uid, boolean isGranted) { 272 273 boolean isChanged; 274 DeviceFilter filter = new DeviceFilter(device); 275 synchronized (mLock) { 276 SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter); 277 if (permissionsForDevice == null) { 278 permissionsForDevice = new SparseBooleanArray(); 279 mDevicePersistentPermissionMap.put(filter, permissionsForDevice); 280 } 281 int idx = permissionsForDevice.indexOfKey(uid); 282 if (idx >= 0) { 283 isChanged = permissionsForDevice.valueAt(idx) != isGranted; 284 permissionsForDevice.setValueAt(idx, isGranted); 285 } else { 286 isChanged = true; 287 permissionsForDevice.put(uid, isGranted); 288 } 289 290 if (isChanged) { 291 scheduleWritePermissionsLocked(); 292 } 293 } 294 } 295 setAccessoryPersistentPermission(@onNull UsbAccessory accessory, int uid, boolean isGranted)296 void setAccessoryPersistentPermission(@NonNull UsbAccessory accessory, int uid, 297 boolean isGranted) { 298 299 boolean isChanged; 300 AccessoryFilter filter = new AccessoryFilter(accessory); 301 synchronized (mLock) { 302 SparseBooleanArray permissionsForAccessory = 303 mAccessoryPersistentPermissionMap.get(filter); 304 if (permissionsForAccessory == null) { 305 permissionsForAccessory = new SparseBooleanArray(); 306 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory); 307 } 308 int idx = permissionsForAccessory.indexOfKey(uid); 309 if (idx >= 0) { 310 isChanged = permissionsForAccessory.valueAt(idx) != isGranted; 311 permissionsForAccessory.setValueAt(idx, isGranted); 312 } else { 313 isChanged = true; 314 permissionsForAccessory.put(uid, isGranted); 315 } 316 317 if (isChanged) { 318 scheduleWritePermissionsLocked(); 319 } 320 } 321 } 322 readPermission(@onNull XmlPullParser parser)323 private void readPermission(@NonNull XmlPullParser parser) throws XmlPullParserException, 324 IOException { 325 int uid; 326 boolean isGranted; 327 328 try { 329 uid = XmlUtils.readIntAttribute(parser, "uid"); 330 } catch (NumberFormatException e) { 331 Slog.e(TAG, "error reading usb permission uid", e); 332 XmlUtils.skipCurrentTag(parser); 333 return; 334 } 335 336 // only use "true"/"false" as valid values 337 String isGrantedString = parser.getAttributeValue(null, "granted"); 338 if (isGrantedString == null || !(isGrantedString.equals(Boolean.TRUE.toString()) 339 || isGrantedString.equals(Boolean.FALSE.toString()))) { 340 Slog.e(TAG, "error reading usb permission granted state"); 341 XmlUtils.skipCurrentTag(parser); 342 return; 343 } 344 isGranted = isGrantedString.equals(Boolean.TRUE.toString()); 345 XmlUtils.nextElement(parser); 346 if ("usb-device".equals(parser.getName())) { 347 DeviceFilter filter = DeviceFilter.read(parser); 348 int idx = mDevicePersistentPermissionMap.indexOfKey(filter); 349 if (idx >= 0) { 350 SparseBooleanArray permissionsForDevice = 351 mDevicePersistentPermissionMap.valueAt(idx); 352 permissionsForDevice.put(uid, isGranted); 353 } else { 354 SparseBooleanArray permissionsForDevice = new SparseBooleanArray(); 355 mDevicePersistentPermissionMap.put(filter, permissionsForDevice); 356 permissionsForDevice.put(uid, isGranted); 357 } 358 } else if ("usb-accessory".equals(parser.getName())) { 359 AccessoryFilter filter = AccessoryFilter.read(parser); 360 int idx = mAccessoryPersistentPermissionMap.indexOfKey(filter); 361 if (idx >= 0) { 362 SparseBooleanArray permissionsForAccessory = 363 mAccessoryPersistentPermissionMap.valueAt(idx); 364 permissionsForAccessory.put(uid, isGranted); 365 } else { 366 SparseBooleanArray permissionsForAccessory = new SparseBooleanArray(); 367 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory); 368 permissionsForAccessory.put(uid, isGranted); 369 } 370 } 371 } 372 373 @GuardedBy("mLock") readPermissionsLocked()374 private void readPermissionsLocked() { 375 mDevicePersistentPermissionMap.clear(); 376 mAccessoryPersistentPermissionMap.clear(); 377 378 try (FileInputStream in = mPermissionsFile.openRead()) { 379 TypedXmlPullParser parser = Xml.resolvePullParser(in); 380 381 XmlUtils.nextElement(parser); 382 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 383 String tagName = parser.getName(); 384 if ("permission".equals(tagName)) { 385 readPermission(parser); 386 } else { 387 XmlUtils.nextElement(parser); 388 } 389 } 390 } catch (FileNotFoundException e) { 391 if (DEBUG) Slog.d(TAG, "usb permissions file not found"); 392 } catch (Exception e) { 393 Slog.e(TAG, "error reading usb permissions file, deleting to start fresh", e); 394 mPermissionsFile.delete(); 395 } 396 } 397 398 @GuardedBy("mLock") scheduleWritePermissionsLocked()399 private void scheduleWritePermissionsLocked() { 400 if (mIsCopyPermissionsScheduled) { 401 return; 402 } 403 mIsCopyPermissionsScheduled = true; 404 405 AsyncTask.execute(() -> { 406 int numDevices; 407 DeviceFilter[] devices; 408 int[][] uidsForDevices; 409 boolean[][] grantedValuesForDevices; 410 411 int numAccessories; 412 AccessoryFilter[] accessories; 413 int[][] uidsForAccessories; 414 boolean[][] grantedValuesForAccessories; 415 416 synchronized (mLock) { 417 // Copy the permission state so we can write outside of lock 418 numDevices = mDevicePersistentPermissionMap.size(); 419 devices = new DeviceFilter[numDevices]; 420 uidsForDevices = new int[numDevices][]; 421 grantedValuesForDevices = new boolean[numDevices][]; 422 for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) { 423 devices[deviceIdx] = 424 new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx)); 425 SparseBooleanArray permissions = 426 mDevicePersistentPermissionMap.valueAt(deviceIdx); 427 int numPermissions = permissions.size(); 428 uidsForDevices[deviceIdx] = new int[numPermissions]; 429 grantedValuesForDevices[deviceIdx] = new boolean[numPermissions]; 430 for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) { 431 uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx); 432 grantedValuesForDevices[deviceIdx][permissionIdx] = 433 permissions.valueAt(permissionIdx); 434 } 435 } 436 437 numAccessories = mAccessoryPersistentPermissionMap.size(); 438 accessories = new AccessoryFilter[numAccessories]; 439 uidsForAccessories = new int[numAccessories][]; 440 grantedValuesForAccessories = new boolean[numAccessories][]; 441 for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) { 442 accessories[accessoryIdx] = new AccessoryFilter( 443 mAccessoryPersistentPermissionMap.keyAt(accessoryIdx)); 444 SparseBooleanArray permissions = 445 mAccessoryPersistentPermissionMap.valueAt(accessoryIdx); 446 int numPermissions = permissions.size(); 447 uidsForAccessories[accessoryIdx] = new int[numPermissions]; 448 grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions]; 449 for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) { 450 uidsForAccessories[accessoryIdx][permissionIdx] = 451 permissions.keyAt(permissionIdx); 452 grantedValuesForAccessories[accessoryIdx][permissionIdx] = 453 permissions.valueAt(permissionIdx); 454 } 455 } 456 mIsCopyPermissionsScheduled = false; 457 } 458 459 synchronized (mPermissionsFile) { 460 FileOutputStream out = null; 461 try { 462 out = mPermissionsFile.startWrite(); 463 TypedXmlSerializer serializer = Xml.resolveSerializer(out); 464 serializer.startDocument(null, true); 465 serializer.startTag(null, "permissions"); 466 467 for (int i = 0; i < numDevices; i++) { 468 int numPermissions = uidsForDevices[i].length; 469 for (int j = 0; j < numPermissions; j++) { 470 serializer.startTag(null, "permission"); 471 serializer.attribute(null, "uid", 472 Integer.toString(uidsForDevices[i][j])); 473 serializer.attribute(null, "granted", 474 Boolean.toString(grantedValuesForDevices[i][j])); 475 devices[i].write(serializer); 476 serializer.endTag(null, "permission"); 477 } 478 } 479 480 for (int i = 0; i < numAccessories; i++) { 481 int numPermissions = uidsForAccessories[i].length; 482 for (int j = 0; j < numPermissions; j++) { 483 serializer.startTag(null, "permission"); 484 serializer.attribute(null, "uid", 485 Integer.toString(uidsForAccessories[i][j])); 486 serializer.attribute(null, "granted", 487 Boolean.toString(grantedValuesForDevices[i][j])); 488 accessories[i].write(serializer); 489 serializer.endTag(null, "permission"); 490 } 491 } 492 493 serializer.endTag(null, "permissions"); 494 serializer.endDocument(); 495 496 mPermissionsFile.finishWrite(out); 497 } catch (IOException e) { 498 Slog.e(TAG, "Failed to write permissions", e); 499 if (out != null) { 500 mPermissionsFile.failWrite(out); 501 } 502 } 503 } 504 }); 505 } 506 507 /** 508 * Creates UI dialog to request permission for the given package to access the device 509 * or accessory. 510 * 511 * @param device The USB device attached 512 * @param accessory The USB accessory attached 513 * @param canBeDefault Whether the calling pacakge can set as default handler 514 * of the USB device or accessory 515 * @param packageName The package name of the calling package 516 * @param uid The uid of the calling package 517 * @param userContext The context to start the UI dialog 518 * @param pi PendingIntent for returning result 519 */ requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, @NonNull String packageName, int uid, @NonNull Context userContext, @NonNull PendingIntent pi)520 void requestPermissionDialog(@Nullable UsbDevice device, 521 @Nullable UsbAccessory accessory, 522 boolean canBeDefault, 523 @NonNull String packageName, 524 int uid, 525 @NonNull Context userContext, 526 @NonNull PendingIntent pi) { 527 final long identity = Binder.clearCallingIdentity(); 528 try { 529 Intent intent = new Intent(); 530 if (device != null) { 531 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 532 } else { 533 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 534 } 535 intent.putExtra(Intent.EXTRA_INTENT, pi); 536 intent.putExtra(Intent.EXTRA_UID, uid); 537 intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault); 538 intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName); 539 intent.setComponent( 540 ComponentName.unflattenFromString(userContext.getResources().getString( 541 com.android.internal.R.string.config_usbPermissionActivity))); 542 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 543 544 userContext.startActivityAsUser(intent, mUser); 545 } catch (ActivityNotFoundException e) { 546 Slog.e(TAG, "unable to start UsbPermissionActivity"); 547 } finally { 548 Binder.restoreCallingIdentity(identity); 549 } 550 } 551 dump(@onNull DualDumpOutputStream dump, String idName, long id)552 void dump(@NonNull DualDumpOutputStream dump, String idName, long id) { 553 long token = dump.start(idName, id); 554 synchronized (mLock) { 555 dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier()); 556 int numMappings = mDevicePermissionMap.size(); 557 for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) { 558 String deviceName = mDevicePermissionMap.keyAt(mappingsIdx); 559 long devicePermissionToken = dump.start("device_permissions", 560 UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS); 561 562 dump.write("device_name", UsbDevicePermissionProto.DEVICE_NAME, deviceName); 563 564 SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx); 565 int numUids = uidList.size(); 566 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) { 567 dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx)); 568 } 569 570 dump.end(devicePermissionToken); 571 } 572 573 numMappings = mAccessoryPermissionMap.size(); 574 for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) { 575 UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx); 576 long accessoryPermissionToken = dump.start("accessory_permissions", 577 UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS); 578 579 dump.write("accessory_description", 580 UsbAccessoryPermissionProto.ACCESSORY_DESCRIPTION, 581 accessory.getDescription()); 582 583 SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx); 584 int numUids = uidList.size(); 585 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) { 586 dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx)); 587 } 588 589 dump.end(accessoryPermissionToken); 590 } 591 592 numMappings = mDevicePersistentPermissionMap.size(); 593 for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) { 594 DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx); 595 long devicePermissionToken = dump.start("device_persistent_permissions", 596 UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS); 597 filter.dump(dump, "device", 598 UsbDevicePersistentPermissionProto.DEVICE_FILTER); 599 SparseBooleanArray permissions = 600 mDevicePersistentPermissionMap.valueAt(mappingsIdx); 601 int numPermissions = permissions.size(); 602 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) { 603 long uidPermissionToken = dump.start("uid_permission", 604 UsbDevicePersistentPermissionProto.PERMISSION_VALUES); 605 dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx)); 606 dump.write("is_granted", 607 UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx)); 608 dump.end(uidPermissionToken); 609 } 610 dump.end(devicePermissionToken); 611 } 612 613 numMappings = mAccessoryPersistentPermissionMap.size(); 614 for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) { 615 AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx); 616 long accessoryPermissionToken = dump.start("accessory_persistent_permissions", 617 UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS); 618 filter.dump(dump, "accessory", 619 UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER); 620 SparseBooleanArray permissions = 621 mAccessoryPersistentPermissionMap.valueAt(mappingsIdx); 622 int numPermissions = permissions.size(); 623 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) { 624 long uidPermissionToken = dump.start("uid_permission", 625 UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES); 626 dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx)); 627 dump.write("is_granted", 628 UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx)); 629 dump.end(uidPermissionToken); 630 } 631 dump.end(accessoryPermissionToken); 632 } 633 } 634 dump.end(token); 635 } 636 637 /** 638 * Check for camera permission of the calling process. 639 * 640 * @param packageName Package name of the caller. 641 * @param pid Linux pid of the calling process. 642 * @param uid Linux uid of the calling process. 643 * @return True in case camera permission is available, False otherwise. 644 */ isCameraPermissionGranted(String packageName, int pid, int uid)645 private boolean isCameraPermissionGranted(String packageName, int pid, int uid) { 646 int targetSdkVersion = android.os.Build.VERSION_CODES.P; 647 try { 648 ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0); 649 // compare uid with packageName to foil apps pretending to be someone else 650 if (aInfo.uid != uid) { 651 Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid); 652 return false; 653 } 654 targetSdkVersion = aInfo.targetSdkVersion; 655 } catch (PackageManager.NameNotFoundException e) { 656 Slog.i(TAG, "Package not found, likely due to invalid package name!"); 657 return false; 658 } 659 660 if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) { 661 int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid); 662 if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) { 663 Slog.i(TAG, "Camera permission required for USB video class devices"); 664 return false; 665 } 666 } 667 668 return true; 669 } 670 checkPermission(UsbDevice device, String packageName, int pid, int uid)671 public void checkPermission(UsbDevice device, String packageName, int pid, int uid) { 672 if (!hasPermission(device, packageName, pid, uid)) { 673 throw new SecurityException("User has not given " + uid + "/" + packageName 674 + " permission to access device " + device.getDeviceName()); 675 } 676 } 677 checkPermission(UsbAccessory accessory, int uid)678 public void checkPermission(UsbAccessory accessory, int uid) { 679 if (!hasPermission(accessory, uid)) { 680 throw new SecurityException("User has not given " + uid + " permission to accessory " 681 + accessory); 682 } 683 } 684 requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, String packageName, PendingIntent pi, int uid)685 private void requestPermissionDialog(@Nullable UsbDevice device, 686 @Nullable UsbAccessory accessory, 687 boolean canBeDefault, 688 String packageName, 689 PendingIntent pi, 690 int uid) { 691 boolean throwException = false; 692 693 // compare uid with packageName to foil apps pretending to be someone else 694 try { 695 ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0); 696 if (aInfo.uid != uid) { 697 Slog.w(TAG, "package " + packageName 698 + " does not match caller's uid " + uid); 699 EventLog.writeEvent(SNET_EVENT_LOG_ID, "180104273", -1, ""); 700 throwException = true; 701 } 702 } catch (PackageManager.NameNotFoundException e) { 703 throwException = true; 704 } finally { 705 if (throwException) 706 throw new IllegalArgumentException("package " + packageName + " not found"); 707 } 708 709 requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi); 710 } 711 requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid, int uid)712 public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid, 713 int uid) { 714 Intent intent = new Intent(); 715 716 // respond immediately if permission has already been granted 717 if (hasPermission(device, packageName, pid, uid)) { 718 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 719 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 720 try { 721 pi.send(mContext, 0, intent); 722 } catch (PendingIntent.CanceledException e) { 723 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 724 } 725 return; 726 } 727 // If the app doesn't have camera permission do not request permission to the USB device. 728 // Note that if the USB camera also has a microphone, a warning will be shown to the user if 729 // the app doesn't have RECORD_AUDIO permission. 730 if (device.getHasVideoCapture()) { 731 if (!isCameraPermissionGranted(packageName, pid, uid)) { 732 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 733 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); 734 try { 735 pi.send(mContext, 0, intent); 736 } catch (PendingIntent.CanceledException e) { 737 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 738 } 739 return; 740 } 741 } 742 743 requestPermissionDialog(device, null, 744 mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid); 745 } 746 requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi, int uid)747 public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi, 748 int uid) { 749 // respond immediately if permission has already been granted 750 if (hasPermission(accessory, uid)) { 751 Intent intent = new Intent(); 752 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 753 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 754 try { 755 pi.send(mContext, 0, intent); 756 } catch (PendingIntent.CanceledException e) { 757 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 758 } 759 return; 760 } 761 762 requestPermissionDialog(null, accessory, 763 mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid); 764 } 765 } 766