• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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