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