• 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 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