1 /* 2 * Copyright (C) 2012 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.bluetooth; 18 19 import static android.Manifest.permission.ACCESS_COARSE_LOCATION; 20 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 21 import static android.Manifest.permission.BLUETOOTH_ADVERTISE; 22 import static android.Manifest.permission.BLUETOOTH_CONNECT; 23 import static android.Manifest.permission.BLUETOOTH_SCAN; 24 import static android.Manifest.permission.RENOUNCE_PERMISSIONS; 25 import static android.bluetooth.BluetoothUtils.USER_HANDLE_NULL; 26 import static android.content.pm.PackageManager.GET_PERMISSIONS; 27 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; 28 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 29 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; 30 import static android.permission.PermissionManager.PERMISSION_HARD_DENIED; 31 32 import android.Manifest; 33 import android.annotation.NonNull; 34 import android.annotation.Nullable; 35 import android.annotation.RequiresPermission; 36 import android.annotation.SuppressLint; 37 import android.app.AppOpsManager; 38 import android.app.BroadcastOptions; 39 import android.bluetooth.BluetoothAdapter; 40 import android.bluetooth.BluetoothDevice; 41 import android.companion.AssociationInfo; 42 import android.companion.CompanionDeviceManager; 43 import android.content.AttributionSource; 44 import android.content.ContentValues; 45 import android.content.Context; 46 import android.content.pm.PackageInfo; 47 import android.content.pm.PackageManager; 48 import android.location.LocationManager; 49 import android.net.Uri; 50 import android.os.Binder; 51 import android.os.Build; 52 import android.os.Bundle; 53 import android.os.ParcelUuid; 54 import android.os.PowerExemptionManager; 55 import android.os.Process; 56 import android.os.SystemProperties; 57 import android.os.UserHandle; 58 import android.os.UserManager; 59 import android.permission.PermissionManager; 60 import android.provider.DeviceConfig; 61 import android.provider.Telephony; 62 import android.util.Log; 63 64 import com.android.bluetooth.btservice.AdapterService; 65 import com.android.bluetooth.btservice.ProfileService; 66 67 import org.xmlpull.v1.XmlPullParser; 68 import org.xmlpull.v1.XmlPullParserException; 69 70 import java.io.IOException; 71 import java.io.InputStream; 72 import java.io.OutputStream; 73 import java.nio.ByteBuffer; 74 import java.nio.ByteOrder; 75 import java.nio.charset.Charset; 76 import java.nio.charset.CharsetDecoder; 77 import java.time.Instant; 78 import java.time.ZoneId; 79 import java.time.format.DateTimeFormatter; 80 import java.util.Objects; 81 import java.util.UUID; 82 import java.util.concurrent.TimeUnit; 83 84 /** 85 * @hide 86 */ 87 public final class Utils { 88 private static final String TAG = "BluetoothUtils"; 89 private static final int MICROS_PER_UNIT = 625; 90 private static final String PTS_TEST_MODE_PROPERTY = "persist.bluetooth.pts"; 91 private static final String KEY_TEMP_ALLOW_LIST_DURATION_MS = "temp_allow_list_duration_ms"; 92 private static final long DEFAULT_TEMP_ALLOW_LIST_DURATION_MS = 20_000; 93 94 static final int BD_ADDR_LEN = 6; // bytes 95 static final int BD_UUID_LEN = 16; // bytes 96 97 /* 98 * Special character 99 * 100 * (See "What is a phone number?" doc) 101 * 'p' --- GSM pause character, same as comma 102 * 'n' --- GSM wild character 103 * 'w' --- GSM wait character 104 */ 105 public static final char PAUSE = ','; 106 public static final char WAIT = ';'; 107 isPause(char c)108 private static boolean isPause(char c) { 109 return c == 'p' || c == 'P'; 110 } 111 isToneWait(char c)112 private static boolean isToneWait(char c) { 113 return c == 'w' || c == 'W'; 114 } 115 getName(@ullable BluetoothDevice device)116 public static @Nullable String getName(@Nullable BluetoothDevice device) { 117 final AdapterService service = AdapterService.getAdapterService(); 118 if (service != null && device != null) { 119 return service.getRemoteName(device); 120 } else { 121 return null; 122 } 123 } 124 getLoggableAddress(@ullable BluetoothDevice device)125 public static String getLoggableAddress(@Nullable BluetoothDevice device) { 126 if (device == null) { 127 return "00:00:00:00:00:00"; 128 } else { 129 return "xx:xx:xx:xx:" + device.toString().substring(12); 130 } 131 } 132 getAddressStringFromByte(byte[] address)133 public static String getAddressStringFromByte(byte[] address) { 134 if (address == null || address.length != BD_ADDR_LEN) { 135 return null; 136 } 137 138 return String.format("%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], address[2], 139 address[3], address[4], address[5]); 140 } 141 getByteAddress(BluetoothDevice device)142 public static byte[] getByteAddress(BluetoothDevice device) { 143 return getBytesFromAddress(device.getAddress()); 144 } 145 getBytesFromAddress(String address)146 public static byte[] getBytesFromAddress(String address) { 147 int i, j = 0; 148 byte[] output = new byte[BD_ADDR_LEN]; 149 150 for (i = 0; i < address.length(); i++) { 151 if (address.charAt(i) != ':') { 152 output[j] = (byte) Integer.parseInt(address.substring(i, i + 2), BD_UUID_LEN); 153 j++; 154 i++; 155 } 156 } 157 158 return output; 159 } 160 byteArrayToInt(byte[] valueBuf)161 public static int byteArrayToInt(byte[] valueBuf) { 162 return byteArrayToInt(valueBuf, 0); 163 } 164 byteArrayToShort(byte[] valueBuf)165 public static short byteArrayToShort(byte[] valueBuf) { 166 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 167 converter.order(ByteOrder.nativeOrder()); 168 return converter.getShort(); 169 } 170 byteArrayToInt(byte[] valueBuf, int offset)171 public static int byteArrayToInt(byte[] valueBuf, int offset) { 172 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 173 converter.order(ByteOrder.nativeOrder()); 174 return converter.getInt(offset); 175 } 176 byteArrayToString(byte[] valueBuf)177 public static String byteArrayToString(byte[] valueBuf) { 178 StringBuilder sb = new StringBuilder(); 179 for (int idx = 0; idx < valueBuf.length; idx++) { 180 if (idx != 0) { 181 sb.append(" "); 182 } 183 sb.append(String.format("%02x", valueBuf[idx])); 184 } 185 return sb.toString(); 186 } 187 188 /** 189 * A parser to transfer a byte array to a UTF8 string 190 * 191 * @param valueBuf the byte array to transfer 192 * @return the transferred UTF8 string 193 */ byteArrayToUtf8String(byte[] valueBuf)194 public static String byteArrayToUtf8String(byte[] valueBuf) { 195 CharsetDecoder decoder = Charset.forName("UTF8").newDecoder(); 196 ByteBuffer byteBuffer = ByteBuffer.wrap(valueBuf); 197 String valueStr = ""; 198 try { 199 valueStr = decoder.decode(byteBuffer).toString(); 200 } catch (Exception ex) { 201 Log.e(TAG, "Error when parsing byte array to UTF8 String. " + ex); 202 } 203 return valueStr; 204 } 205 intToByteArray(int value)206 public static byte[] intToByteArray(int value) { 207 ByteBuffer converter = ByteBuffer.allocate(4); 208 converter.order(ByteOrder.nativeOrder()); 209 converter.putInt(value); 210 return converter.array(); 211 } 212 uuidToByteArray(ParcelUuid pUuid)213 public static byte[] uuidToByteArray(ParcelUuid pUuid) { 214 int length = BD_UUID_LEN; 215 ByteBuffer converter = ByteBuffer.allocate(length); 216 converter.order(ByteOrder.BIG_ENDIAN); 217 long msb, lsb; 218 UUID uuid = pUuid.getUuid(); 219 msb = uuid.getMostSignificantBits(); 220 lsb = uuid.getLeastSignificantBits(); 221 converter.putLong(msb); 222 converter.putLong(8, lsb); 223 return converter.array(); 224 } 225 uuidsToByteArray(ParcelUuid[] uuids)226 public static byte[] uuidsToByteArray(ParcelUuid[] uuids) { 227 int length = uuids.length * BD_UUID_LEN; 228 ByteBuffer converter = ByteBuffer.allocate(length); 229 converter.order(ByteOrder.BIG_ENDIAN); 230 UUID uuid; 231 long msb, lsb; 232 for (int i = 0; i < uuids.length; i++) { 233 uuid = uuids[i].getUuid(); 234 msb = uuid.getMostSignificantBits(); 235 lsb = uuid.getLeastSignificantBits(); 236 converter.putLong(i * BD_UUID_LEN, msb); 237 converter.putLong(i * BD_UUID_LEN + 8, lsb); 238 } 239 return converter.array(); 240 } 241 byteArrayToUuid(byte[] val)242 public static ParcelUuid[] byteArrayToUuid(byte[] val) { 243 int numUuids = val.length / BD_UUID_LEN; 244 ParcelUuid[] puuids = new ParcelUuid[numUuids]; 245 UUID uuid; 246 int offset = 0; 247 248 ByteBuffer converter = ByteBuffer.wrap(val); 249 converter.order(ByteOrder.BIG_ENDIAN); 250 251 for (int i = 0; i < numUuids; i++) { 252 puuids[i] = new ParcelUuid( 253 new UUID(converter.getLong(offset), converter.getLong(offset + 8))); 254 offset += BD_UUID_LEN; 255 } 256 return puuids; 257 } 258 debugGetAdapterStateString(int state)259 public static String debugGetAdapterStateString(int state) { 260 switch (state) { 261 case BluetoothAdapter.STATE_OFF: 262 return "STATE_OFF"; 263 case BluetoothAdapter.STATE_ON: 264 return "STATE_ON"; 265 case BluetoothAdapter.STATE_TURNING_ON: 266 return "STATE_TURNING_ON"; 267 case BluetoothAdapter.STATE_TURNING_OFF: 268 return "STATE_TURNING_OFF"; 269 default: 270 return "UNKNOWN"; 271 } 272 } 273 ellipsize(String s)274 public static String ellipsize(String s) { 275 // Only ellipsize release builds 276 if (!Build.TYPE.equals("user")) { 277 return s; 278 } 279 if (s == null) { 280 return null; 281 } 282 if (s.length() < 3) { 283 return s; 284 } 285 return s.charAt(0) + "⋯" + s.charAt(s.length() - 1); 286 } 287 copyStream(InputStream is, OutputStream os, int bufferSize)288 public static void copyStream(InputStream is, OutputStream os, int bufferSize) 289 throws IOException { 290 if (is != null && os != null) { 291 byte[] buffer = new byte[bufferSize]; 292 int bytesRead = 0; 293 while ((bytesRead = is.read(buffer)) >= 0) { 294 os.write(buffer, 0, bytesRead); 295 } 296 } 297 } 298 safeCloseStream(InputStream is)299 public static void safeCloseStream(InputStream is) { 300 if (is != null) { 301 try { 302 is.close(); 303 } catch (Throwable t) { 304 Log.d(TAG, "Error closing stream", t); 305 } 306 } 307 } 308 safeCloseStream(OutputStream os)309 public static void safeCloseStream(OutputStream os) { 310 if (os != null) { 311 try { 312 os.close(); 313 } catch (Throwable t) { 314 Log.d(TAG, "Error closing stream", t); 315 } 316 } 317 } 318 319 static int sSystemUiUid = USER_HANDLE_NULL.getIdentifier(); setSystemUiUid(int uid)320 public static void setSystemUiUid(int uid) { 321 Utils.sSystemUiUid = uid; 322 } 323 324 static int sForegroundUserId = USER_HANDLE_NULL.getIdentifier(); 325 getForegroundUserId()326 public static int getForegroundUserId() { 327 return Utils.sForegroundUserId; 328 } 329 setForegroundUserId(int userId)330 public static void setForegroundUserId(int userId) { 331 Utils.sForegroundUserId = userId; 332 } 333 334 /** 335 * Enforces that a Companion Device Manager (CDM) association exists between the calling 336 * application and the Bluetooth Device. 337 * 338 * @param cdm the CompanionDeviceManager object 339 * @param context the Bluetooth AdapterService context 340 * @param callingPackage the calling package 341 * @param callingUid the calling app uid 342 * @param device the remote BluetoothDevice 343 * @return {@code true} if there is a CDM association 344 * @throws SecurityException if the package name does not match the uid or the association 345 * doesn't exist 346 */ enforceCdmAssociation(CompanionDeviceManager cdm, Context context, String callingPackage, int callingUid, BluetoothDevice device)347 public static boolean enforceCdmAssociation(CompanionDeviceManager cdm, Context context, 348 String callingPackage, int callingUid, BluetoothDevice device) { 349 if (!isPackageNameAccurate(context, callingPackage, callingUid)) { 350 throw new SecurityException("hasCdmAssociation: Package name " + callingPackage 351 + " is inaccurate for calling uid " + callingUid); 352 } 353 354 for (AssociationInfo association : cdm.getAllAssociations()) { 355 if (association.getPackageName().equals(callingPackage) 356 && !association.isSelfManaged() && device.getAddress() != null 357 && association.getDeviceMacAddress() != null 358 && device.getAddress().equalsIgnoreCase( 359 association.getDeviceMacAddress().toString())) { 360 return true; 361 } 362 } 363 throw new SecurityException("The application with package name " + callingPackage 364 + " does not have a CDM association with the Bluetooth Device"); 365 } 366 367 /** 368 * Verifies whether the calling package name matches the calling app uid 369 * @param context the Bluetooth AdapterService context 370 * @param callingPackage the calling application package name 371 * @param callingUid the calling application uid 372 * @return {@code true} if the package name matches the calling app uid, {@code false} otherwise 373 */ isPackageNameAccurate(Context context, String callingPackage, int callingUid)374 public static boolean isPackageNameAccurate(Context context, String callingPackage, 375 int callingUid) { 376 UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid); 377 378 // Verifies the integrity of the calling package name 379 try { 380 int packageUid = context.createContextAsUser(callingUser, 0) 381 .getPackageManager().getPackageUid(callingPackage, 0); 382 if (packageUid != callingUid) { 383 Log.e(TAG, "isPackageNameAccurate: App with package name " + callingPackage 384 + " is UID " + packageUid + " but caller is " + callingUid); 385 return false; 386 } 387 } catch (PackageManager.NameNotFoundException e) { 388 Log.e(TAG, "isPackageNameAccurate: App with package name " + callingPackage 389 + " does not exist"); 390 return false; 391 } 392 return true; 393 } 394 395 /** 396 * Checks whether the caller has the BLUETOOTH_PRIVILEGED permission 397 * 398 * @param context the Bluetooth AdapterService context 399 * @return {@code true} if the caller has the BLUETOOTH_PRIVILEGED permission, {@code false} 400 * otherwise 401 */ 402 // Suppressed since we're not actually enforcing here 403 @SuppressLint("AndroidFrameworkRequiresPermission") hasBluetoothPrivilegedPermission(Context context)404 public static boolean hasBluetoothPrivilegedPermission(Context context) { 405 return context.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) 406 == PackageManager.PERMISSION_GRANTED; 407 } 408 409 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) enforceBluetoothPrivilegedPermission(Context context)410 public static void enforceBluetoothPrivilegedPermission(Context context) { 411 context.enforceCallingOrSelfPermission( 412 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 413 "Need BLUETOOTH PRIVILEGED permission"); 414 } 415 416 @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) enforceLocalMacAddressPermission(Context context)417 public static void enforceLocalMacAddressPermission(Context context) { 418 context.enforceCallingOrSelfPermission( 419 android.Manifest.permission.LOCAL_MAC_ADDRESS, 420 "Need LOCAL_MAC_ADDRESS permission"); 421 } 422 423 @RequiresPermission(android.Manifest.permission.DUMP) enforceDumpPermission(Context context)424 public static void enforceDumpPermission(Context context) { 425 context.enforceCallingOrSelfPermission( 426 android.Manifest.permission.DUMP, 427 "Need DUMP permission"); 428 } 429 getCallingAttributionSource(Context context)430 public static AttributionSource getCallingAttributionSource(Context context) { 431 int callingUid = Binder.getCallingUid(); 432 if (callingUid == android.os.Process.ROOT_UID) { 433 callingUid = android.os.Process.SYSTEM_UID; 434 } 435 return new AttributionSource.Builder(callingUid) 436 .setPackageName(context.getPackageManager().getPackagesForUid(callingUid)[0]) 437 .build(); 438 } 439 440 @SuppressLint("AndroidFrameworkRequiresPermission") checkPermissionForPreflight(Context context, String permission)441 private static boolean checkPermissionForPreflight(Context context, String permission) { 442 PermissionManager pm = context.getSystemService(PermissionManager.class); 443 if (pm == null) { 444 return false; 445 } 446 final int result = pm.checkPermissionForPreflight(permission, 447 context.getAttributionSource()); 448 if (result == PERMISSION_GRANTED) { 449 return true; 450 } 451 452 final String msg = "Need " + permission + " permission"; 453 if (result == PERMISSION_HARD_DENIED) { 454 throw new SecurityException(msg); 455 } else { 456 Log.w(TAG, msg); 457 return false; 458 } 459 } 460 461 @SuppressLint("AndroidFrameworkRequiresPermission") checkPermissionForDataDelivery(Context context, String permission, AttributionSource attributionSource, String message)462 private static boolean checkPermissionForDataDelivery(Context context, String permission, 463 AttributionSource attributionSource, String message) { 464 if (isInstrumentationTestMode()) { 465 return true; 466 } 467 // STOPSHIP(b/188391719): enable this security enforcement 468 // attributionSource.enforceCallingUid(); 469 AttributionSource currentAttribution = new AttributionSource 470 .Builder(context.getAttributionSource()) 471 .setNext(attributionSource) 472 .build(); 473 PermissionManager pm = context.getSystemService(PermissionManager.class); 474 if (pm == null) { 475 return false; 476 } 477 final int result = pm.checkPermissionForDataDeliveryFromDataSource(permission, 478 currentAttribution, message); 479 if (result == PERMISSION_GRANTED) { 480 return true; 481 } 482 483 final String msg = "Need " + permission + " permission for " + attributionSource + ": " 484 + message; 485 if (result == PERMISSION_HARD_DENIED) { 486 throw new SecurityException(msg); 487 } else { 488 Log.w(TAG, msg); 489 return false; 490 } 491 } 492 493 /** 494 * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns 495 * false if the result is a soft denial. Throws SecurityException if the result is a hard 496 * denial. 497 * 498 * <p>Should be used in situations where the app op should not be noted. 499 */ 500 @SuppressLint("AndroidFrameworkRequiresPermission") 501 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) checkConnectPermissionForPreflight(Context context)502 public static boolean checkConnectPermissionForPreflight(Context context) { 503 return checkPermissionForPreflight(context, BLUETOOTH_CONNECT); 504 } 505 506 /** 507 * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns 508 * false if the result is a soft denial. Throws SecurityException if the result is a hard 509 * denial. 510 * 511 * <p>Should be used in situations where data will be delivered and hence the app op should 512 * be noted. 513 */ 514 @SuppressLint("AndroidFrameworkRequiresPermission") 515 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) checkConnectPermissionForDataDelivery( Context context, AttributionSource attributionSource, String message)516 public static boolean checkConnectPermissionForDataDelivery( 517 Context context, AttributionSource attributionSource, String message) { 518 return checkPermissionForDataDelivery(context, BLUETOOTH_CONNECT, 519 attributionSource, message); 520 } 521 522 /** 523 * Returns true if the BLUETOOTH_SCAN permission is granted for the calling app. Returns false 524 * if the result is a soft denial. Throws SecurityException if the result is a hard denial. 525 * 526 * <p>Should be used in situations where the app op should not be noted. 527 */ 528 @SuppressLint("AndroidFrameworkRequiresPermission") 529 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) checkScanPermissionForPreflight(Context context)530 public static boolean checkScanPermissionForPreflight(Context context) { 531 return checkPermissionForPreflight(context, BLUETOOTH_SCAN); 532 } 533 534 /** 535 * Returns true if the BLUETOOTH_SCAN permission is granted for the calling app. Returns false 536 * if the result is a soft denial. Throws SecurityException if the result is a hard denial. 537 * 538 * <p>Should be used in situations where data will be delivered and hence the app op should 539 * be noted. 540 */ 541 @SuppressLint("AndroidFrameworkRequiresPermission") 542 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) checkScanPermissionForDataDelivery( Context context, AttributionSource attributionSource, String message)543 public static boolean checkScanPermissionForDataDelivery( 544 Context context, AttributionSource attributionSource, String message) { 545 return checkPermissionForDataDelivery(context, BLUETOOTH_SCAN, 546 attributionSource, message); 547 } 548 549 /** 550 * Returns true if the BLUETOOTH_ADVERTISE permission is granted for the 551 * calling app. Returns false if the result is a soft denial. Throws 552 * SecurityException if the result is a hard denial. 553 * <p> 554 * Should be used in situations where the app op should not be noted. 555 */ 556 @SuppressLint("AndroidFrameworkRequiresPermission") 557 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) checkAdvertisePermissionForPreflight(Context context)558 public static boolean checkAdvertisePermissionForPreflight(Context context) { 559 return checkPermissionForPreflight(context, BLUETOOTH_ADVERTISE); 560 } 561 562 /** 563 * Returns true if the BLUETOOTH_ADVERTISE permission is granted for the 564 * calling app. Returns false if the result is a soft denial. Throws 565 * SecurityException if the result is a hard denial. 566 * <p> 567 * Should be used in situations where data will be delivered and hence the 568 * app op should be noted. 569 */ 570 @SuppressLint("AndroidFrameworkRequiresPermission") 571 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) checkAdvertisePermissionForDataDelivery( Context context, AttributionSource attributionSource, String message)572 public static boolean checkAdvertisePermissionForDataDelivery( 573 Context context, AttributionSource attributionSource, String message) { 574 return checkPermissionForDataDelivery(context, BLUETOOTH_ADVERTISE, 575 attributionSource, message); 576 } 577 578 /** 579 * Returns true if the specified package has disavowed the use of bluetooth scans for location, 580 * that is, if they have specified the {@code neverForLocation} flag on the BLUETOOTH_SCAN 581 * permission. 582 */ 583 // Suppressed since we're not actually enforcing here 584 @SuppressLint("AndroidFrameworkRequiresPermission") hasDisavowedLocationForScan( Context context, AttributionSource attributionSource, boolean inTestMode)585 public static boolean hasDisavowedLocationForScan( 586 Context context, AttributionSource attributionSource, boolean inTestMode) { 587 588 // Check every step along the attribution chain for a renouncement. 589 // If location has been renounced anywhere in the chain we treat it as a disavowal. 590 AttributionSource currentAttrib = attributionSource; 591 while (true) { 592 if (currentAttrib.getRenouncedPermissions().contains(ACCESS_FINE_LOCATION) 593 && (inTestMode || context.checkPermission(RENOUNCE_PERMISSIONS, -1, 594 currentAttrib.getUid()) 595 == PackageManager.PERMISSION_GRANTED)) { 596 return true; 597 } 598 AttributionSource nextAttrib = currentAttrib.getNext(); 599 if (nextAttrib == null) { 600 break; 601 } 602 currentAttrib = nextAttrib; 603 } 604 605 // Check the last attribution in the chain for a neverForLocation disavowal. 606 String packageName = currentAttrib.getPackageName(); 607 PackageManager pm = context.getPackageManager(); 608 try { 609 // TODO(b/183478032): Cache PackageInfo for use here. 610 PackageInfo pkgInfo = 611 pm.getPackageInfo(packageName, GET_PERMISSIONS | MATCH_UNINSTALLED_PACKAGES); 612 for (int i = 0; i < pkgInfo.requestedPermissions.length; i++) { 613 if (pkgInfo.requestedPermissions[i].equals(BLUETOOTH_SCAN)) { 614 return (pkgInfo.requestedPermissionsFlags[i] 615 & PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION) != 0; 616 } 617 } 618 } catch (PackageManager.NameNotFoundException e) { 619 Log.w(TAG, "Could not find package for disavowal check: " + packageName); 620 } 621 return false; 622 } 623 checkCallerIsSystemOrActiveUser()624 private static boolean checkCallerIsSystemOrActiveUser() { 625 int callingUid = Binder.getCallingUid(); 626 UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid); 627 628 return (sForegroundUserId == callingUser.getIdentifier()) 629 || (UserHandle.getAppId(sSystemUiUid) == UserHandle.getAppId(callingUid)) 630 || (UserHandle.getAppId(Process.SYSTEM_UID) == UserHandle.getAppId(callingUid)); 631 } 632 checkCallerIsSystemOrActiveUser(String tag)633 public static boolean checkCallerIsSystemOrActiveUser(String tag) { 634 final boolean res = checkCallerIsSystemOrActiveUser(); 635 if (!res) { 636 Log.w(TAG, tag + " - Not allowed for non-active user and non-system user"); 637 } 638 return res; 639 } 640 callerIsSystemOrActiveUser(String tag, String method)641 public static boolean callerIsSystemOrActiveUser(String tag, String method) { 642 return checkCallerIsSystemOrActiveUser(tag + "." + method + "()"); 643 } 644 checkCallerIsSystemOrActiveOrManagedUser(Context context)645 private static boolean checkCallerIsSystemOrActiveOrManagedUser(Context context) { 646 if (context == null) { 647 return checkCallerIsSystemOrActiveUser(); 648 } 649 int callingUid = Binder.getCallingUid(); 650 UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid); 651 652 // Use the Bluetooth process identity when making call to get parent user 653 final long ident = Binder.clearCallingIdentity(); 654 try { 655 UserManager um = context.getSystemService(UserManager.class); 656 UserHandle uh = um.getProfileParent(callingUser); 657 int parentUser = (uh != null) ? uh.getIdentifier() : USER_HANDLE_NULL.getIdentifier(); 658 659 // Always allow SystemUI/System access. 660 return (sForegroundUserId == callingUser.getIdentifier()) 661 || (sForegroundUserId == parentUser) 662 || (UserHandle.getAppId(sSystemUiUid) == UserHandle.getAppId(callingUid)) 663 || (UserHandle.getAppId(Process.SYSTEM_UID) == UserHandle.getAppId(callingUid)); 664 } catch (Exception ex) { 665 Log.e(TAG, "checkCallerAllowManagedProfiles: Exception ex=" + ex); 666 return false; 667 } finally { 668 Binder.restoreCallingIdentity(ident); 669 } 670 } 671 checkCallerIsSystemOrActiveOrManagedUser(Context context, String tag)672 public static boolean checkCallerIsSystemOrActiveOrManagedUser(Context context, String tag) { 673 if (isInstrumentationTestMode()) { 674 return true; 675 } 676 final boolean res = checkCallerIsSystemOrActiveOrManagedUser(context); 677 if (!res) { 678 Log.w(TAG, tag + " - Not allowed for" 679 + " non-active user and non-system and non-managed user"); 680 } 681 return res; 682 } 683 callerIsSystemOrActiveOrManagedUser(Context context, String tag, String method)684 public static boolean callerIsSystemOrActiveOrManagedUser(Context context, String tag, 685 String method) { 686 return checkCallerIsSystemOrActiveOrManagedUser(context, tag + "." + method + "()"); 687 } 688 checkServiceAvailable(ProfileService service, String tag)689 public static boolean checkServiceAvailable(ProfileService service, String tag) { 690 if (service == null) { 691 Log.w(TAG, tag + " - Not present"); 692 return false; 693 } 694 if (!service.isAvailable()) { 695 Log.w(TAG, tag + " - Not available"); 696 return false; 697 } 698 return true; 699 } 700 701 /** 702 * Checks whether location is off and must be on for us to perform some operation 703 */ blockedByLocationOff(Context context, UserHandle userHandle)704 public static boolean blockedByLocationOff(Context context, UserHandle userHandle) { 705 return !context.getSystemService(LocationManager.class) 706 .isLocationEnabledForUser(userHandle); 707 } 708 709 /** 710 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION and 711 * OP_COARSE_LOCATION is allowed 712 */ 713 // Suppressed since we're not actually enforcing here 714 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasCoarseLocation( Context context, AttributionSource attributionSource, UserHandle userHandle)715 public static boolean checkCallerHasCoarseLocation( 716 Context context, AttributionSource attributionSource, UserHandle userHandle) { 717 if (blockedByLocationOff(context, userHandle)) { 718 Log.e(TAG, "Permission denial: Location is off."); 719 return false; 720 } 721 AttributionSource currentAttribution = new AttributionSource 722 .Builder(context.getAttributionSource()) 723 .setNext(attributionSource) 724 .build(); 725 // STOPSHIP(b/188391719): enable this security enforcement 726 // attributionSource.enforceCallingUid(); 727 PermissionManager pm = context.getSystemService(PermissionManager.class); 728 if (pm == null) { 729 return false; 730 } 731 if (pm.checkPermissionForDataDeliveryFromDataSource(ACCESS_COARSE_LOCATION, 732 currentAttribution, "Bluetooth location check") == PERMISSION_GRANTED) { 733 return true; 734 } 735 736 Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION " 737 + "permission to get scan results"); 738 return false; 739 } 740 741 /** 742 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION and 743 * OP_COARSE_LOCATION is allowed or android.Manifest.permission.ACCESS_FINE_LOCATION and 744 * OP_FINE_LOCATION is allowed 745 */ 746 // Suppressed since we're not actually enforcing here 747 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasCoarseOrFineLocation( Context context, AttributionSource attributionSource, UserHandle userHandle)748 public static boolean checkCallerHasCoarseOrFineLocation( 749 Context context, AttributionSource attributionSource, UserHandle userHandle) { 750 if (blockedByLocationOff(context, userHandle)) { 751 Log.e(TAG, "Permission denial: Location is off."); 752 return false; 753 } 754 755 final AttributionSource currentAttribution = new AttributionSource 756 .Builder(context.getAttributionSource()) 757 .setNext(attributionSource) 758 .build(); 759 // STOPSHIP(b/188391719): enable this security enforcement 760 // attributionSource.enforceCallingUid(); 761 PermissionManager pm = context.getSystemService(PermissionManager.class); 762 if (pm == null) { 763 return false; 764 } 765 if (pm.checkPermissionForDataDeliveryFromDataSource(ACCESS_FINE_LOCATION, 766 currentAttribution, "Bluetooth location check") == PERMISSION_GRANTED) { 767 return true; 768 } 769 770 if (pm.checkPermissionForDataDeliveryFromDataSource(ACCESS_COARSE_LOCATION, 771 currentAttribution, "Bluetooth location check") == PERMISSION_GRANTED) { 772 return true; 773 } 774 775 Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION" 776 + "permission to get scan results"); 777 return false; 778 } 779 780 /** 781 * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION and 782 * OP_FINE_LOCATION is allowed 783 */ 784 // Suppressed since we're not actually enforcing here 785 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasFineLocation( Context context, AttributionSource attributionSource, UserHandle userHandle)786 public static boolean checkCallerHasFineLocation( 787 Context context, AttributionSource attributionSource, UserHandle userHandle) { 788 if (blockedByLocationOff(context, userHandle)) { 789 Log.e(TAG, "Permission denial: Location is off."); 790 return false; 791 } 792 793 AttributionSource currentAttribution = new AttributionSource 794 .Builder(context.getAttributionSource()) 795 .setNext(attributionSource) 796 .build(); 797 // STOPSHIP(b/188391719): enable this security enforcement 798 // attributionSource.enforceCallingUid(); 799 PermissionManager pm = context.getSystemService(PermissionManager.class); 800 if (pm == null) { 801 return false; 802 } 803 if (pm.checkPermissionForDataDeliveryFromDataSource(ACCESS_FINE_LOCATION, 804 currentAttribution, "Bluetooth location check") == PERMISSION_GRANTED) { 805 return true; 806 } 807 808 Log.e(TAG, "Permission denial: Need ACCESS_FINE_LOCATION " 809 + "permission to get scan results"); 810 return false; 811 } 812 813 /** 814 * Returns true if the caller holds NETWORK_SETTINGS 815 */ 816 // Suppressed since we're not actually enforcing here 817 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasNetworkSettingsPermission(Context context)818 public static boolean checkCallerHasNetworkSettingsPermission(Context context) { 819 return context.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS) 820 == PackageManager.PERMISSION_GRANTED; 821 } 822 823 /** 824 * Returns true if the caller holds NETWORK_SETUP_WIZARD 825 */ 826 // Suppressed since we're not actually enforcing here 827 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasNetworkSetupWizardPermission(Context context)828 public static boolean checkCallerHasNetworkSetupWizardPermission(Context context) { 829 return context.checkCallingOrSelfPermission( 830 android.Manifest.permission.NETWORK_SETUP_WIZARD) 831 == PackageManager.PERMISSION_GRANTED; 832 } 833 834 /** 835 * Returns true if the caller holds RADIO_SCAN_WITHOUT_LOCATION 836 */ 837 // Suppressed since we're not actually enforcing here 838 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasScanWithoutLocationPermission(Context context)839 public static boolean checkCallerHasScanWithoutLocationPermission(Context context) { 840 return context.checkCallingOrSelfPermission( 841 android.Manifest.permission.RADIO_SCAN_WITHOUT_LOCATION) 842 == PackageManager.PERMISSION_GRANTED; 843 } 844 845 // Suppressed since we're not actually enforcing here 846 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasPrivilegedPermission(Context context)847 public static boolean checkCallerHasPrivilegedPermission(Context context) { 848 return context.checkCallingOrSelfPermission( 849 android.Manifest.permission.BLUETOOTH_PRIVILEGED) 850 == PackageManager.PERMISSION_GRANTED; 851 } 852 853 // Suppressed since we're not actually enforcing here 854 @SuppressLint("AndroidFrameworkRequiresPermission") checkCallerHasWriteSmsPermission(Context context)855 public static boolean checkCallerHasWriteSmsPermission(Context context) { 856 return context.checkCallingOrSelfPermission( 857 android.Manifest.permission.WRITE_SMS) == PackageManager.PERMISSION_GRANTED; 858 } 859 860 /** 861 * Checks that the target sdk of the app corresponding to the provided package name is greater 862 * than or equal to the passed in target sdk. 863 * <p> 864 * For example, if the calling app has target SDK {@link Build.VERSION_CODES#S} and we pass in 865 * the targetSdk {@link Build.VERSION_CODES#R}, the API will return true because S >= R. 866 * 867 * @param context Bluetooth service context 868 * @param pkgName caller's package name 869 * @param expectedMinimumTargetSdk one of the values from {@link Build.VERSION_CODES} 870 * @return {@code true} if the caller's target sdk is greater than or equal to 871 * expectedMinimumTargetSdk, {@code false} otherwise 872 */ checkCallerTargetSdk(Context context, String pkgName, int expectedMinimumTargetSdk)873 public static boolean checkCallerTargetSdk(Context context, String pkgName, 874 int expectedMinimumTargetSdk) { 875 try { 876 return context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion 877 >= expectedMinimumTargetSdk; 878 } catch (PackageManager.NameNotFoundException e) { 879 // In case of exception, assume true 880 } 881 return true; 882 } 883 isAppOppAllowed(AppOpsManager appOps, String op, String callingPackage, @NonNull String callingFeatureId)884 private static boolean isAppOppAllowed(AppOpsManager appOps, String op, String callingPackage, 885 @NonNull String callingFeatureId) { 886 return appOps.noteOp(op, Binder.getCallingUid(), callingPackage, callingFeatureId, null) 887 == AppOpsManager.MODE_ALLOWED; 888 } 889 890 /** 891 * Converts {@code millisecond} to unit. Each unit is 0.625 millisecond. 892 */ millsToUnit(int milliseconds)893 public static int millsToUnit(int milliseconds) { 894 return (int) (TimeUnit.MILLISECONDS.toMicros(milliseconds) / MICROS_PER_UNIT); 895 } 896 897 /** 898 * Check if we are running in BluetoothInstrumentationTest context by trying to load 899 * com.android.bluetooth.FileSystemWriteTest. If we are not in Instrumentation test mode, this 900 * class should not be found. Thus, the assumption is that FileSystemWriteTest must exist. 901 * If FileSystemWriteTest is removed in the future, another test class in 902 * BluetoothInstrumentationTest should be used instead 903 * 904 * @return true if in BluetoothInstrumentationTest, false otherwise 905 */ isInstrumentationTestMode()906 public static boolean isInstrumentationTestMode() { 907 try { 908 return Class.forName("com.android.bluetooth.FileSystemWriteTest") != null; 909 } catch (ClassNotFoundException exception) { 910 return false; 911 } 912 } 913 914 /** 915 * Throws {@link IllegalStateException} if we are not in BluetoothInstrumentationTest. Useful 916 * for ensuring certain methods only get called in BluetoothInstrumentationTest 917 */ enforceInstrumentationTestMode()918 public static void enforceInstrumentationTestMode() { 919 if (!isInstrumentationTestMode()) { 920 throw new IllegalStateException("Not in BluetoothInstrumentationTest"); 921 } 922 } 923 924 /** 925 * Check if we are running in PTS test mode. To enable/disable PTS test mode, invoke 926 * {@code adb shell setprop persist.bluetooth.pts true/false} 927 * 928 * @return true if in PTS Test mode, false otherwise 929 */ isPtsTestMode()930 public static boolean isPtsTestMode() { 931 return SystemProperties.getBoolean(PTS_TEST_MODE_PROPERTY, false); 932 } 933 934 /** 935 * Get uid/pid string in a binder call 936 * 937 * @return "uid/pid=xxxx/yyyy" 938 */ getUidPidString()939 public static String getUidPidString() { 940 return "uid/pid=" + Binder.getCallingUid() + "/" + Binder.getCallingPid(); 941 } 942 943 /** 944 * Get system local time 945 * 946 * @return "MM-dd HH:mm:ss.SSS" 947 */ getLocalTimeString()948 public static String getLocalTimeString() { 949 return DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS") 950 .withZone(ZoneId.systemDefault()).format(Instant.now()); 951 } 952 skipCurrentTag(XmlPullParser parser)953 public static void skipCurrentTag(XmlPullParser parser) 954 throws XmlPullParserException, IOException { 955 int outerDepth = parser.getDepth(); 956 int type; 957 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 958 && (type != XmlPullParser.END_TAG 959 || parser.getDepth() > outerDepth)) { 960 } 961 } 962 963 /** 964 * Converts pause and tonewait pause characters 965 * to Android representation. 966 * RFC 3601 says pause is 'p' and tonewait is 'w'. 967 */ convertPreDial(String phoneNumber)968 public static String convertPreDial(String phoneNumber) { 969 if (phoneNumber == null) { 970 return null; 971 } 972 int len = phoneNumber.length(); 973 StringBuilder ret = new StringBuilder(len); 974 975 for (int i = 0; i < len; i++) { 976 char c = phoneNumber.charAt(i); 977 978 if (isPause(c)) { 979 c = PAUSE; 980 } else if (isToneWait(c)) { 981 c = WAIT; 982 } 983 ret.append(c); 984 } 985 return ret.toString(); 986 } 987 988 /** 989 * Move a message to the given folder. 990 * 991 * @param context the context to use 992 * @param uri the message to move 993 * @param messageSent if the message is SENT or FAILED 994 * @return true if the operation succeeded 995 */ moveMessageToFolder(Context context, Uri uri, boolean messageSent)996 public static boolean moveMessageToFolder(Context context, Uri uri, boolean messageSent) { 997 if (uri == null) { 998 return false; 999 } 1000 1001 ContentValues values = new ContentValues(3); 1002 if (messageSent) { 1003 values.put(Telephony.Sms.READ, 1); 1004 values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_SENT); 1005 } else { 1006 values.put(Telephony.Sms.READ, 0); 1007 values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_FAILED); 1008 } 1009 values.put(Telephony.Sms.ERROR_CODE, 0); 1010 1011 return 1 == BluetoothMethodProxy.getInstance().contentResolverUpdate( 1012 context.getContentResolver(), uri, values, null, null); 1013 } 1014 1015 /** 1016 * Returns bundled broadcast options. 1017 */ getTempAllowlistBroadcastOptions()1018 public static @NonNull Bundle getTempAllowlistBroadcastOptions() { 1019 return getTempBroadcastOptions().toBundle(); 1020 } 1021 1022 /** 1023 * Returns broadcast options. 1024 */ getTempBroadcastOptions()1025 public static @NonNull BroadcastOptions getTempBroadcastOptions() { 1026 // Use the Bluetooth process identity to pass permission check when reading DeviceConfig 1027 final long ident = Binder.clearCallingIdentity(); 1028 final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); 1029 try { 1030 final long durationMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_BLUETOOTH, 1031 KEY_TEMP_ALLOW_LIST_DURATION_MS, DEFAULT_TEMP_ALLOW_LIST_DURATION_MS); 1032 bOptions.setTemporaryAppAllowlist(durationMs, 1033 TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, 1034 PowerExemptionManager.REASON_BLUETOOTH_BROADCAST, ""); 1035 } finally { 1036 Binder.restoreCallingIdentity(ident); 1037 } 1038 return bOptions; 1039 } 1040 /** 1041 * Checks that value is present as at least one of the elements of the array. 1042 * @param array the array to check in 1043 * @param value the value to check for 1044 * @return true if the value is present in the array 1045 */ arrayContains(@ullable T[] array, T value)1046 public static <T> boolean arrayContains(@Nullable T[] array, T value) { 1047 if (array == null) return false; 1048 for (T element : array) { 1049 if (Objects.equals(element, value)) return true; 1050 } 1051 return false; 1052 } 1053 } 1054