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 android.app.ActivityManager; 20 import android.app.AppOpsManager; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.content.Context; 24 import android.content.ContextWrapper; 25 import android.content.pm.PackageManager; 26 import android.content.pm.UserInfo; 27 import android.location.LocationManager; 28 import android.os.Binder; 29 import android.os.Build; 30 import android.os.ParcelUuid; 31 import android.os.Process; 32 import android.os.SystemProperties; 33 import android.os.UserHandle; 34 import android.os.UserManager; 35 import android.util.Log; 36 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.OutputStream; 40 import java.nio.ByteBuffer; 41 import java.nio.ByteOrder; 42 import java.nio.charset.Charset; 43 import java.nio.charset.CharsetDecoder; 44 import java.util.List; 45 import java.util.UUID; 46 import java.util.concurrent.TimeUnit; 47 48 /** 49 * @hide 50 */ 51 52 public final class Utils { 53 private static final String TAG = "BluetoothUtils"; 54 private static final int MICROS_PER_UNIT = 625; 55 private static final String PTS_TEST_MODE_PROPERTY = "persist.bluetooth.pts"; 56 57 static final int BD_ADDR_LEN = 6; // bytes 58 static final int BD_UUID_LEN = 16; // bytes 59 getAddressStringFromByte(byte[] address)60 public static String getAddressStringFromByte(byte[] address) { 61 if (address == null || address.length != BD_ADDR_LEN) { 62 return null; 63 } 64 65 return String.format("%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], address[2], 66 address[3], address[4], address[5]); 67 } 68 getByteAddress(BluetoothDevice device)69 public static byte[] getByteAddress(BluetoothDevice device) { 70 return getBytesFromAddress(device.getAddress()); 71 } 72 getBytesFromAddress(String address)73 public static byte[] getBytesFromAddress(String address) { 74 int i, j = 0; 75 byte[] output = new byte[BD_ADDR_LEN]; 76 77 for (i = 0; i < address.length(); i++) { 78 if (address.charAt(i) != ':') { 79 output[j] = (byte) Integer.parseInt(address.substring(i, i + 2), BD_UUID_LEN); 80 j++; 81 i++; 82 } 83 } 84 85 return output; 86 } 87 byteArrayToInt(byte[] valueBuf)88 public static int byteArrayToInt(byte[] valueBuf) { 89 return byteArrayToInt(valueBuf, 0); 90 } 91 byteArrayToShort(byte[] valueBuf)92 public static short byteArrayToShort(byte[] valueBuf) { 93 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 94 converter.order(ByteOrder.nativeOrder()); 95 return converter.getShort(); 96 } 97 byteArrayToInt(byte[] valueBuf, int offset)98 public static int byteArrayToInt(byte[] valueBuf, int offset) { 99 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 100 converter.order(ByteOrder.nativeOrder()); 101 return converter.getInt(offset); 102 } 103 byteArrayToString(byte[] valueBuf)104 public static String byteArrayToString(byte[] valueBuf) { 105 StringBuilder sb = new StringBuilder(); 106 for (int idx = 0; idx < valueBuf.length; idx++) { 107 if (idx != 0) { 108 sb.append(" "); 109 } 110 sb.append(String.format("%02x", valueBuf[idx])); 111 } 112 return sb.toString(); 113 } 114 115 /** 116 * A parser to transfer a byte array to a UTF8 string 117 * 118 * @param valueBuf the byte array to transfer 119 * @return the transferred UTF8 string 120 */ byteArrayToUtf8String(byte[] valueBuf)121 public static String byteArrayToUtf8String(byte[] valueBuf) { 122 CharsetDecoder decoder = Charset.forName("UTF8").newDecoder(); 123 ByteBuffer byteBuffer = ByteBuffer.wrap(valueBuf); 124 String valueStr = ""; 125 try { 126 valueStr = decoder.decode(byteBuffer).toString(); 127 } catch (Exception ex) { 128 Log.e(TAG, "Error when parsing byte array to UTF8 String. " + ex); 129 } 130 return valueStr; 131 } 132 intToByteArray(int value)133 public static byte[] intToByteArray(int value) { 134 ByteBuffer converter = ByteBuffer.allocate(4); 135 converter.order(ByteOrder.nativeOrder()); 136 converter.putInt(value); 137 return converter.array(); 138 } 139 uuidToByteArray(ParcelUuid pUuid)140 public static byte[] uuidToByteArray(ParcelUuid pUuid) { 141 int length = BD_UUID_LEN; 142 ByteBuffer converter = ByteBuffer.allocate(length); 143 converter.order(ByteOrder.BIG_ENDIAN); 144 long msb, lsb; 145 UUID uuid = pUuid.getUuid(); 146 msb = uuid.getMostSignificantBits(); 147 lsb = uuid.getLeastSignificantBits(); 148 converter.putLong(msb); 149 converter.putLong(8, lsb); 150 return converter.array(); 151 } 152 uuidsToByteArray(ParcelUuid[] uuids)153 public static byte[] uuidsToByteArray(ParcelUuid[] uuids) { 154 int length = uuids.length * BD_UUID_LEN; 155 ByteBuffer converter = ByteBuffer.allocate(length); 156 converter.order(ByteOrder.BIG_ENDIAN); 157 UUID uuid; 158 long msb, lsb; 159 for (int i = 0; i < uuids.length; i++) { 160 uuid = uuids[i].getUuid(); 161 msb = uuid.getMostSignificantBits(); 162 lsb = uuid.getLeastSignificantBits(); 163 converter.putLong(i * BD_UUID_LEN, msb); 164 converter.putLong(i * BD_UUID_LEN + 8, lsb); 165 } 166 return converter.array(); 167 } 168 byteArrayToUuid(byte[] val)169 public static ParcelUuid[] byteArrayToUuid(byte[] val) { 170 int numUuids = val.length / BD_UUID_LEN; 171 ParcelUuid[] puuids = new ParcelUuid[numUuids]; 172 UUID uuid; 173 int offset = 0; 174 175 ByteBuffer converter = ByteBuffer.wrap(val); 176 converter.order(ByteOrder.BIG_ENDIAN); 177 178 for (int i = 0; i < numUuids; i++) { 179 puuids[i] = new ParcelUuid( 180 new UUID(converter.getLong(offset), converter.getLong(offset + 8))); 181 offset += BD_UUID_LEN; 182 } 183 return puuids; 184 } 185 debugGetAdapterStateString(int state)186 public static String debugGetAdapterStateString(int state) { 187 switch (state) { 188 case BluetoothAdapter.STATE_OFF: 189 return "STATE_OFF"; 190 case BluetoothAdapter.STATE_ON: 191 return "STATE_ON"; 192 case BluetoothAdapter.STATE_TURNING_ON: 193 return "STATE_TURNING_ON"; 194 case BluetoothAdapter.STATE_TURNING_OFF: 195 return "STATE_TURNING_OFF"; 196 default: 197 return "UNKNOWN"; 198 } 199 } 200 ellipsize(String s)201 public static String ellipsize(String s) { 202 // Only ellipsize release builds 203 if (!Build.TYPE.equals("user")) { 204 return s; 205 } 206 if (s == null) { 207 return null; 208 } 209 if (s.length() < 3) { 210 return s; 211 } 212 return s.charAt(0) + "⋯" + s.charAt(s.length() - 1); 213 } 214 copyStream(InputStream is, OutputStream os, int bufferSize)215 public static void copyStream(InputStream is, OutputStream os, int bufferSize) 216 throws IOException { 217 if (is != null && os != null) { 218 byte[] buffer = new byte[bufferSize]; 219 int bytesRead = 0; 220 while ((bytesRead = is.read(buffer)) >= 0) { 221 os.write(buffer, 0, bytesRead); 222 } 223 } 224 } 225 safeCloseStream(InputStream is)226 public static void safeCloseStream(InputStream is) { 227 if (is != null) { 228 try { 229 is.close(); 230 } catch (Throwable t) { 231 Log.d(TAG, "Error closing stream", t); 232 } 233 } 234 } 235 safeCloseStream(OutputStream os)236 public static void safeCloseStream(OutputStream os) { 237 if (os != null) { 238 try { 239 os.close(); 240 } catch (Throwable t) { 241 Log.d(TAG, "Error closing stream", t); 242 } 243 } 244 } 245 246 static int sSystemUiUid = UserHandle.USER_NULL; setSystemUiUid(int uid)247 public static void setSystemUiUid(int uid) { 248 Utils.sSystemUiUid = uid; 249 } 250 251 static int sForegroundUserId = UserHandle.USER_NULL; setForegroundUserId(int uid)252 public static void setForegroundUserId(int uid) { 253 Utils.sForegroundUserId = uid; 254 } 255 checkCaller()256 public static boolean checkCaller() { 257 int callingUser = UserHandle.getCallingUserId(); 258 int callingUid = Binder.getCallingUid(); 259 return (sForegroundUserId == callingUser) || (sSystemUiUid == callingUid) 260 || (Process.SYSTEM_UID == callingUid); 261 } 262 checkCallerAllowManagedProfiles(Context mContext)263 public static boolean checkCallerAllowManagedProfiles(Context mContext) { 264 if (mContext == null) { 265 return checkCaller(); 266 } 267 int callingUser = UserHandle.getCallingUserId(); 268 int callingUid = Binder.getCallingUid(); 269 270 // Use the Bluetooth process identity when making call to get parent user 271 long ident = Binder.clearCallingIdentity(); 272 try { 273 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 274 UserInfo ui = um.getProfileParent(callingUser); 275 int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL; 276 277 // Always allow SystemUI/System access. 278 return (sForegroundUserId == callingUser) || (sForegroundUserId == parentUser) 279 || (sSystemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid); 280 } catch (Exception ex) { 281 Log.e(TAG, "checkCallerAllowManagedProfiles: Exception ex=" + ex); 282 return false; 283 } finally { 284 Binder.restoreCallingIdentity(ident); 285 } 286 } 287 288 /** 289 * Enforce the context has android.Manifest.permission.BLUETOOTH_ADMIN permission. A 290 * {@link SecurityException} would be thrown if neither the calling process or the application 291 * does not have BLUETOOTH_ADMIN permission. 292 * 293 * @param context Context for the permission check. 294 */ enforceAdminPermission(ContextWrapper context)295 public static void enforceAdminPermission(ContextWrapper context) { 296 context.enforceCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_ADMIN, 297 "Need BLUETOOTH_ADMIN permission"); 298 } 299 300 /** 301 * Checks whether location is off and must be on for us to perform some operation 302 */ blockedByLocationOff(Context context, UserHandle userHandle)303 public static boolean blockedByLocationOff(Context context, UserHandle userHandle) { 304 return !context.getSystemService(LocationManager.class) 305 .isLocationEnabledForUser(userHandle); 306 } 307 308 /** 309 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION and 310 * OP_COARSE_LOCATION is allowed 311 */ checkCallerHasCoarseLocation(Context context, AppOpsManager appOps, String callingPackage, UserHandle userHandle)312 public static boolean checkCallerHasCoarseLocation(Context context, AppOpsManager appOps, 313 String callingPackage, UserHandle userHandle) { 314 if (blockedByLocationOff(context, userHandle)) { 315 Log.e(TAG, "Permission denial: Location is off."); 316 return false; 317 } 318 319 // Check coarse, but note fine 320 if (context.checkCallingOrSelfPermission( 321 android.Manifest.permission.ACCESS_COARSE_LOCATION) 322 == PackageManager.PERMISSION_GRANTED 323 && isAppOppAllowed(appOps, AppOpsManager.OP_FINE_LOCATION, callingPackage)) { 324 return true; 325 } 326 327 Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION " 328 + "permission to get scan results"); 329 return false; 330 } 331 332 /** 333 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION and 334 * OP_COARSE_LOCATION is allowed or android.Manifest.permission.ACCESS_FINE_LOCATION and 335 * OP_FINE_LOCATION is allowed 336 */ checkCallerHasCoarseOrFineLocation(Context context, AppOpsManager appOps, String callingPackage, UserHandle userHandle)337 public static boolean checkCallerHasCoarseOrFineLocation(Context context, AppOpsManager appOps, 338 String callingPackage, UserHandle userHandle) { 339 if (blockedByLocationOff(context, userHandle)) { 340 Log.e(TAG, "Permission denial: Location is off."); 341 return false; 342 } 343 344 if (context.checkCallingOrSelfPermission( 345 android.Manifest.permission.ACCESS_FINE_LOCATION) 346 == PackageManager.PERMISSION_GRANTED 347 && isAppOppAllowed(appOps, AppOpsManager.OP_FINE_LOCATION, callingPackage)) { 348 return true; 349 } 350 351 // Check coarse, but note fine 352 if (context.checkCallingOrSelfPermission( 353 android.Manifest.permission.ACCESS_COARSE_LOCATION) 354 == PackageManager.PERMISSION_GRANTED 355 && isAppOppAllowed(appOps, AppOpsManager.OP_FINE_LOCATION, callingPackage)) { 356 return true; 357 } 358 359 Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION" 360 + "permission to get scan results"); 361 return false; 362 } 363 364 /** 365 * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION and 366 * OP_FINE_LOCATION is allowed 367 */ checkCallerHasFineLocation(Context context, AppOpsManager appOps, String callingPackage, UserHandle userHandle)368 public static boolean checkCallerHasFineLocation(Context context, AppOpsManager appOps, 369 String callingPackage, UserHandle userHandle) { 370 if (blockedByLocationOff(context, userHandle)) { 371 Log.e(TAG, "Permission denial: Location is off."); 372 return false; 373 } 374 375 if (context.checkCallingOrSelfPermission( 376 android.Manifest.permission.ACCESS_FINE_LOCATION) 377 == PackageManager.PERMISSION_GRANTED 378 && isAppOppAllowed(appOps, AppOpsManager.OP_FINE_LOCATION, callingPackage)) { 379 return true; 380 } 381 382 Log.e(TAG, "Permission denial: Need ACCESS_FINE_LOCATION " 383 + "permission to get scan results"); 384 return false; 385 } 386 387 /** 388 * Returns true if the caller holds NETWORK_SETTINGS 389 */ checkCallerHasNetworkSettingsPermission(Context context)390 public static boolean checkCallerHasNetworkSettingsPermission(Context context) { 391 return context.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS) 392 == PackageManager.PERMISSION_GRANTED; 393 } 394 395 /** 396 * Returns true if the caller holds NETWORK_SETUP_WIZARD 397 */ checkCallerHasNetworkSetupWizardPermission(Context context)398 public static boolean checkCallerHasNetworkSetupWizardPermission(Context context) { 399 return context.checkCallingOrSelfPermission( 400 android.Manifest.permission.NETWORK_SETUP_WIZARD) 401 == PackageManager.PERMISSION_GRANTED; 402 } 403 isLegacyForegroundApp(Context context, String pkgName)404 public static boolean isLegacyForegroundApp(Context context, String pkgName) { 405 return !isMApp(context, pkgName) && isForegroundApp(context, pkgName); 406 } 407 isMApp(Context context, String pkgName)408 private static boolean isMApp(Context context, String pkgName) { 409 try { 410 return context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion 411 >= Build.VERSION_CODES.M; 412 } catch (PackageManager.NameNotFoundException e) { 413 // In case of exception, assume M app 414 } 415 return true; 416 } 417 isQApp(Context context, String pkgName)418 public static boolean isQApp(Context context, String pkgName) { 419 try { 420 return context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion 421 >= Build.VERSION_CODES.Q; 422 } catch (PackageManager.NameNotFoundException e) { 423 // In case of exception, assume Q app 424 } 425 return true; 426 } 427 /** 428 * Return true if the specified package name is a foreground app. 429 * 430 * @param pkgName application package name. 431 */ isForegroundApp(Context context, String pkgName)432 private static boolean isForegroundApp(Context context, String pkgName) { 433 ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 434 List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); 435 return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); 436 } 437 isAppOppAllowed(AppOpsManager appOps, int op, String callingPackage)438 private static boolean isAppOppAllowed(AppOpsManager appOps, int op, String callingPackage) { 439 return appOps.noteOp(op, Binder.getCallingUid(), callingPackage) 440 == AppOpsManager.MODE_ALLOWED; 441 } 442 443 /** 444 * Converts {@code millisecond} to unit. Each unit is 0.625 millisecond. 445 */ millsToUnit(int milliseconds)446 public static int millsToUnit(int milliseconds) { 447 return (int) (TimeUnit.MILLISECONDS.toMicros(milliseconds) / MICROS_PER_UNIT); 448 } 449 450 /** 451 * Check if we are running in BluetoothInstrumentationTest context by trying to load 452 * com.android.bluetooth.FileSystemWriteTest. If we are not in Instrumentation test mode, this 453 * class should not be found. Thus, the assumption is that FileSystemWriteTest must exist. 454 * If FileSystemWriteTest is removed in the future, another test class in 455 * BluetoothInstrumentationTest should be used instead 456 * 457 * @return true if in BluetoothInstrumentationTest, false otherwise 458 */ isInstrumentationTestMode()459 public static boolean isInstrumentationTestMode() { 460 try { 461 return Class.forName("com.android.bluetooth.FileSystemWriteTest") != null; 462 } catch (ClassNotFoundException exception) { 463 return false; 464 } 465 } 466 467 /** 468 * Throws {@link IllegalStateException} if we are not in BluetoothInstrumentationTest. Useful 469 * for ensuring certain methods only get called in BluetoothInstrumentationTest 470 */ enforceInstrumentationTestMode()471 public static void enforceInstrumentationTestMode() { 472 if (!isInstrumentationTestMode()) { 473 throw new IllegalStateException("Not in BluetoothInstrumentationTest"); 474 } 475 } 476 477 /** 478 * Check if we are running in PTS test mode. To enable/disable PTS test mode, invoke 479 * {@code adb shell setprop persist.bluetooth.pts true/false} 480 * 481 * @return true if in PTS Test mode, false otherwise 482 */ isPtsTestMode()483 public static boolean isPtsTestMode() { 484 return SystemProperties.getBoolean(PTS_TEST_MODE_PROPERTY, false); 485 } 486 487 /** 488 * Get uid/pid string in a binder call 489 * 490 * @return "uid/pid=xxxx/yyyy" 491 */ getUidPidString()492 public static String getUidPidString() { 493 return "uid/pid=" + Binder.getCallingUid() + "/" + Binder.getCallingPid(); 494 } 495 } 496