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.ActivityThread; 21 import android.app.AppOpsManager; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.content.Context; 25 import android.content.ContextWrapper; 26 import android.content.pm.PackageManager; 27 import android.content.pm.UserInfo; 28 import android.os.Binder; 29 import android.os.Build; 30 import android.os.ParcelUuid; 31 import android.os.Process; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.util.Log; 35 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.io.OutputStream; 39 import java.nio.ByteBuffer; 40 import java.nio.ByteOrder; 41 import java.util.List; 42 import java.util.UUID; 43 import java.util.concurrent.TimeUnit; 44 45 /** 46 * @hide 47 */ 48 49 final public class Utils { 50 private static final String TAG = "BluetoothUtils"; 51 private static final int MICROS_PER_UNIT = 625; 52 53 static final int BD_ADDR_LEN = 6; // bytes 54 static final int BD_UUID_LEN = 16; // bytes 55 56 public static String getAddressStringFromByte(byte[] address) { 57 if (address == null || address.length != BD_ADDR_LEN) { 58 return null; 59 } 60 61 return String.format("%02X:%02X:%02X:%02X:%02X:%02X", 62 address[0], address[1], address[2], address[3], address[4], 63 address[5]); 64 } 65 66 public static byte[] getByteAddress(BluetoothDevice device) { 67 return getBytesFromAddress(device.getAddress()); 68 } 69 70 public static byte[] getBytesFromAddress(String address) { 71 int i, j = 0; 72 byte[] output = new byte[BD_ADDR_LEN]; 73 74 for (i = 0; i < address.length(); i++) { 75 if (address.charAt(i) != ':') { 76 output[j] = (byte) Integer.parseInt(address.substring(i, i + 2), BD_UUID_LEN); 77 j++; 78 i++; 79 } 80 } 81 82 return output; 83 } 84 85 public static int byteArrayToInt(byte[] valueBuf) { 86 return byteArrayToInt(valueBuf, 0); 87 } 88 89 public static short byteArrayToShort(byte[] valueBuf) { 90 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 91 converter.order(ByteOrder.nativeOrder()); 92 return converter.getShort(); 93 } 94 95 public static int byteArrayToInt(byte[] valueBuf, int offset) { 96 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 97 converter.order(ByteOrder.nativeOrder()); 98 return converter.getInt(offset); 99 } 100 101 public static String byteArrayToString(byte[] valueBuf) { 102 StringBuilder sb = new StringBuilder(); 103 for (int idx = 0; idx < valueBuf.length; idx++) { 104 if (idx != 0) sb.append(" "); 105 sb.append(String.format("%02x", valueBuf[idx])); 106 } 107 return sb.toString(); 108 } 109 110 public static byte[] intToByteArray(int value) { 111 ByteBuffer converter = ByteBuffer.allocate(4); 112 converter.order(ByteOrder.nativeOrder()); 113 converter.putInt(value); 114 return converter.array(); 115 } 116 117 public static byte[] uuidToByteArray(ParcelUuid pUuid) { 118 int length = BD_UUID_LEN; 119 ByteBuffer converter = ByteBuffer.allocate(length); 120 converter.order(ByteOrder.BIG_ENDIAN); 121 long msb, lsb; 122 UUID uuid = pUuid.getUuid(); 123 msb = uuid.getMostSignificantBits(); 124 lsb = uuid.getLeastSignificantBits(); 125 converter.putLong(msb); 126 converter.putLong(8, lsb); 127 return converter.array(); 128 } 129 130 public static byte[] uuidsToByteArray(ParcelUuid[] uuids) { 131 int length = uuids.length * BD_UUID_LEN; 132 ByteBuffer converter = ByteBuffer.allocate(length); 133 converter.order(ByteOrder.BIG_ENDIAN); 134 UUID uuid; 135 long msb, lsb; 136 for (int i = 0; i < uuids.length; i++) { 137 uuid = uuids[i].getUuid(); 138 msb = uuid.getMostSignificantBits(); 139 lsb = uuid.getLeastSignificantBits(); 140 converter.putLong(i * BD_UUID_LEN, msb); 141 converter.putLong(i * BD_UUID_LEN + 8, lsb); 142 } 143 return converter.array(); 144 } 145 146 public static ParcelUuid[] byteArrayToUuid(byte[] val) { 147 int numUuids = val.length / BD_UUID_LEN; 148 ParcelUuid[] puuids = new ParcelUuid[numUuids]; 149 UUID uuid; 150 int offset = 0; 151 152 ByteBuffer converter = ByteBuffer.wrap(val); 153 converter.order(ByteOrder.BIG_ENDIAN); 154 155 for (int i = 0; i < numUuids; i++) { 156 puuids[i] = new ParcelUuid(new UUID(converter.getLong(offset), 157 converter.getLong(offset + 8))); 158 offset += BD_UUID_LEN; 159 } 160 return puuids; 161 } 162 163 public static String debugGetAdapterStateString(int state) { 164 switch (state) { 165 case BluetoothAdapter.STATE_OFF: 166 return "STATE_OFF"; 167 case BluetoothAdapter.STATE_ON: 168 return "STATE_ON"; 169 case BluetoothAdapter.STATE_TURNING_ON: 170 return "STATE_TURNING_ON"; 171 case BluetoothAdapter.STATE_TURNING_OFF: 172 return "STATE_TURNING_OFF"; 173 default: 174 return "UNKNOWN"; 175 } 176 } 177 178 public static String ellipsize(String s) { 179 // Only ellipsize release builds 180 if (!Build.TYPE.equals("user")) return s; 181 if (s == null) return null; 182 if (s.length() < 3) return s; 183 return s.charAt(0) + "" + s.charAt(s.length() - 1); 184 } 185 186 public static void copyStream(InputStream is, OutputStream os, int bufferSize) 187 throws IOException { 188 if (is != null && os != null) { 189 byte[] buffer = new byte[bufferSize]; 190 int bytesRead = 0; 191 while ((bytesRead = is.read(buffer)) >= 0) { 192 os.write(buffer, 0, bytesRead); 193 } 194 } 195 } 196 197 public static void safeCloseStream(InputStream is) { 198 if (is != null) { 199 try { 200 is.close(); 201 } catch (Throwable t) { 202 Log.d(TAG, "Error closing stream", t); 203 } 204 } 205 } 206 207 public static void safeCloseStream(OutputStream os) { 208 if (os != null) { 209 try { 210 os.close(); 211 } catch (Throwable t) { 212 Log.d(TAG, "Error closing stream", t); 213 } 214 } 215 } 216 217 public static boolean checkCaller() { 218 boolean ok; 219 // Get the caller's user id then clear the calling identity 220 // which will be restored in the finally clause. 221 int callingUser = UserHandle.getCallingUserId(); 222 int callingUid = Binder.getCallingUid(); 223 long ident = Binder.clearCallingIdentity(); 224 225 try { 226 // With calling identity cleared the current user is the foreground user. 227 int foregroundUser = ActivityManager.getCurrentUser(); 228 ok = (foregroundUser == callingUser); 229 if (!ok) { 230 // Always allow SystemUI/System access. 231 final int systemUiUid = ActivityThread.getPackageManager().getPackageUid( 232 "com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, 233 UserHandle.USER_SYSTEM); 234 ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid); 235 } 236 } catch (Exception ex) { 237 Log.e(TAG, "checkIfCallerIsSelfOrForegroundUser: Exception ex=" + ex); 238 ok = false; 239 } finally { 240 Binder.restoreCallingIdentity(ident); 241 } 242 return ok; 243 } 244 245 public static boolean checkCallerAllowManagedProfiles(Context mContext) { 246 if (mContext == null) { 247 return checkCaller(); 248 } 249 boolean ok; 250 // Get the caller's user id and if it's a managed profile, get it's parents 251 // id, then clear the calling identity 252 // which will be restored in the finally clause. 253 int callingUser = UserHandle.getCallingUserId(); 254 int callingUid = Binder.getCallingUid(); 255 long ident = Binder.clearCallingIdentity(); 256 try { 257 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 258 UserInfo ui = um.getProfileParent(callingUser); 259 int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL; 260 // With calling identity cleared the current user is the foreground user. 261 int foregroundUser = ActivityManager.getCurrentUser(); 262 ok = (foregroundUser == callingUser) || 263 (foregroundUser == parentUser); 264 if (!ok) { 265 // Always allow SystemUI/System access. 266 final int systemUiUid = ActivityThread.getPackageManager().getPackageUid( 267 "com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, 268 UserHandle.USER_SYSTEM); 269 ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid); 270 } 271 } catch (Exception ex) { 272 Log.e(TAG, "checkCallerAllowManagedProfiles: Exception ex=" + ex); 273 ok = false; 274 } finally { 275 Binder.restoreCallingIdentity(ident); 276 } 277 return ok; 278 } 279 280 /** 281 * Enforce the context has android.Manifest.permission.BLUETOOTH_ADMIN permission. A 282 * {@link SecurityException} would be thrown if neither the calling process or the application 283 * does not have BLUETOOTH_ADMIN permission. 284 * 285 * @param context Context for the permission check. 286 */ 287 public static void enforceAdminPermission(ContextWrapper context) { 288 context.enforceCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_ADMIN, 289 "Need BLUETOOTH_ADMIN permission"); 290 } 291 292 /** 293 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION or 294 * android.Manifest.permission.ACCESS_FINE_LOCATION and a corresponding app op is allowed 295 */ 296 public static boolean checkCallerHasLocationPermission(Context context, AppOpsManager appOps, 297 String callingPackage) { 298 if (context.checkCallingOrSelfPermission(android.Manifest.permission. 299 ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED 300 && isAppOppAllowed(appOps, AppOpsManager.OP_FINE_LOCATION, callingPackage)) { 301 return true; 302 } 303 304 if (context.checkCallingOrSelfPermission(android.Manifest.permission. 305 ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED 306 && isAppOppAllowed(appOps, AppOpsManager.OP_COARSE_LOCATION, callingPackage)) { 307 return true; 308 } 309 // Enforce location permission for apps targeting M and later versions 310 if (isMApp(context, callingPackage)) { 311 // PEERS_MAC_ADDRESS is another way to get scan results without 312 // requiring location permissions, so only throw an exception here 313 // if PEERS_MAC_ADDRESS permission is missing as well 314 if (!checkCallerHasPeersMacAddressPermission(context)) { 315 throw new SecurityException("Need ACCESS_COARSE_LOCATION or " 316 + "ACCESS_FINE_LOCATION permission to get scan results"); 317 } 318 } else { 319 // Pre-M apps running in the foreground should continue getting scan results 320 if (isForegroundApp(context, callingPackage)) { 321 return true; 322 } 323 Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION " 324 + "permission to get scan results"); 325 } 326 return false; 327 } 328 329 /** 330 * Returns true if the caller holds PEERS_MAC_ADDRESS. 331 */ 332 public static boolean checkCallerHasPeersMacAddressPermission(Context context) { 333 return context.checkCallingOrSelfPermission( 334 android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED; 335 } 336 337 public static boolean isLegacyForegroundApp(Context context, String pkgName) { 338 return !isMApp(context, pkgName) && isForegroundApp(context, pkgName); 339 } 340 341 private static boolean isMApp(Context context, String pkgName) { 342 try { 343 return context.getPackageManager().getApplicationInfo(pkgName, 0) 344 .targetSdkVersion >= Build.VERSION_CODES.M; 345 } catch (PackageManager.NameNotFoundException e) { 346 // In case of exception, assume M app 347 } 348 return true; 349 } 350 351 /** 352 * Return true if the specified package name is a foreground app. 353 * 354 * @param pkgName application package name. 355 */ 356 private static boolean isForegroundApp(Context context, String pkgName) { 357 ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); 358 List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); 359 return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); 360 } 361 362 private static boolean isAppOppAllowed(AppOpsManager appOps, int op, String callingPackage) { 363 return appOps.noteOp(op, Binder.getCallingUid(), callingPackage) 364 == AppOpsManager.MODE_ALLOWED; 365 } 366 367 /** 368 * Converts {@code millisecond} to unit. Each unit is 0.625 millisecond. 369 */ 370 public static int millsToUnit(int milliseconds) { 371 return (int) (TimeUnit.MILLISECONDS.toMicros(milliseconds) / MICROS_PER_UNIT); 372 } 373 } 374