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