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