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