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
17package com.android.bluetooth;
18
19import android.app.ActivityManager;
20import android.app.ActivityThread;
21import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothDevice;
23import android.content.Context;
24import android.content.ContextWrapper;
25import android.content.pm.UserInfo;
26import android.os.Binder;
27import android.os.ParcelUuid;
28import android.os.Process;
29import android.os.UserHandle;
30import android.os.UserManager;
31import android.util.Log;
32
33import java.io.IOException;
34import java.io.InputStream;
35import java.io.OutputStream;
36import java.nio.ByteBuffer;
37import java.nio.ByteOrder;
38import java.util.UUID;
39import java.util.concurrent.TimeUnit;
40
41/**
42 * @hide
43 */
44
45final public class Utils {
46    private static final String TAG = "BluetoothUtils";
47    private static final int MICROS_PER_UNIT = 625;
48
49    static final int BD_ADDR_LEN = 6; // bytes
50    static final int BD_UUID_LEN = 16; // bytes
51
52    public static String getAddressStringFromByte(byte[] address) {
53        if (address == null || address.length != BD_ADDR_LEN) {
54            return null;
55        }
56
57        return String.format("%02X:%02X:%02X:%02X:%02X:%02X",
58                address[0], address[1], address[2], address[3], address[4],
59                address[5]);
60    }
61
62    public static byte[] getByteAddress(BluetoothDevice device) {
63        return getBytesFromAddress(device.getAddress());
64    }
65
66    public static byte[] getBytesFromAddress(String address) {
67        int i, j = 0;
68        byte[] output = new byte[BD_ADDR_LEN];
69
70        for (i = 0; i < address.length(); i++) {
71            if (address.charAt(i) != ':') {
72                output[j] = (byte) Integer.parseInt(address.substring(i, i + 2), BD_UUID_LEN);
73                j++;
74                i++;
75            }
76        }
77
78        return output;
79    }
80
81    public static int byteArrayToInt(byte[] valueBuf) {
82        return byteArrayToInt(valueBuf, 0);
83    }
84
85    public static short byteArrayToShort(byte[] valueBuf) {
86        ByteBuffer converter = ByteBuffer.wrap(valueBuf);
87        converter.order(ByteOrder.nativeOrder());
88        return converter.getShort();
89    }
90
91    public static int byteArrayToInt(byte[] valueBuf, int offset) {
92        ByteBuffer converter = ByteBuffer.wrap(valueBuf);
93        converter.order(ByteOrder.nativeOrder());
94        return converter.getInt(offset);
95    }
96
97    public static byte[] intToByteArray(int value) {
98        ByteBuffer converter = ByteBuffer.allocate(4);
99        converter.order(ByteOrder.nativeOrder());
100        converter.putInt(value);
101        return converter.array();
102    }
103
104    public static byte[] uuidToByteArray(ParcelUuid pUuid) {
105        int length = BD_UUID_LEN;
106        ByteBuffer converter = ByteBuffer.allocate(length);
107        converter.order(ByteOrder.BIG_ENDIAN);
108        long msb, lsb;
109        UUID uuid = pUuid.getUuid();
110        msb = uuid.getMostSignificantBits();
111        lsb = uuid.getLeastSignificantBits();
112        converter.putLong(msb);
113        converter.putLong(8, lsb);
114        return converter.array();
115    }
116
117    public static byte[] uuidsToByteArray(ParcelUuid[] uuids) {
118        int length = uuids.length * BD_UUID_LEN;
119        ByteBuffer converter = ByteBuffer.allocate(length);
120        converter.order(ByteOrder.BIG_ENDIAN);
121        UUID uuid;
122        long msb, lsb;
123        for (int i = 0; i < uuids.length; i++) {
124            uuid = uuids[i].getUuid();
125            msb = uuid.getMostSignificantBits();
126            lsb = uuid.getLeastSignificantBits();
127            converter.putLong(i * BD_UUID_LEN, msb);
128            converter.putLong(i * BD_UUID_LEN + 8, lsb);
129        }
130        return converter.array();
131    }
132
133    public static ParcelUuid[] byteArrayToUuid(byte[] val) {
134        int numUuids = val.length / BD_UUID_LEN;
135        ParcelUuid[] puuids = new ParcelUuid[numUuids];
136        UUID uuid;
137        int offset = 0;
138
139        ByteBuffer converter = ByteBuffer.wrap(val);
140        converter.order(ByteOrder.BIG_ENDIAN);
141
142        for (int i = 0; i < numUuids; i++) {
143            puuids[i] = new ParcelUuid(new UUID(converter.getLong(offset),
144                    converter.getLong(offset + 8)));
145            offset += BD_UUID_LEN;
146        }
147        return puuids;
148    }
149
150    public static String debugGetAdapterStateString(int state) {
151        switch (state) {
152            case BluetoothAdapter.STATE_OFF:
153                return "STATE_OFF";
154            case BluetoothAdapter.STATE_ON:
155                return "STATE_ON";
156            case BluetoothAdapter.STATE_TURNING_ON:
157                return "STATE_TURNING_ON";
158            case BluetoothAdapter.STATE_TURNING_OFF:
159                return "STATE_TURNING_OFF";
160            default:
161                return "UNKNOWN";
162        }
163    }
164
165    public static void copyStream(InputStream is, OutputStream os, int bufferSize)
166            throws IOException {
167        if (is != null && os != null) {
168            byte[] buffer = new byte[bufferSize];
169            int bytesRead = 0;
170            while ((bytesRead = is.read(buffer)) >= 0) {
171                os.write(buffer, 0, bytesRead);
172            }
173        }
174    }
175
176    public static void safeCloseStream(InputStream is) {
177        if (is != null) {
178            try {
179                is.close();
180            } catch (Throwable t) {
181                Log.d(TAG, "Error closing stream", t);
182            }
183        }
184    }
185
186    public static void safeCloseStream(OutputStream os) {
187        if (os != null) {
188            try {
189                os.close();
190            } catch (Throwable t) {
191                Log.d(TAG, "Error closing stream", t);
192            }
193        }
194    }
195
196    public static boolean checkCaller() {
197        boolean ok;
198        // Get the caller's user id then clear the calling identity
199        // which will be restored in the finally clause.
200        int callingUser = UserHandle.getCallingUserId();
201        int callingUid = Binder.getCallingUid();
202        long ident = Binder.clearCallingIdentity();
203
204        try {
205            // With calling identity cleared the current user is the foreground user.
206            int foregroundUser = ActivityManager.getCurrentUser();
207            ok = (foregroundUser == callingUser);
208            if (!ok) {
209                // Always allow SystemUI/System access.
210                int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
211                        "com.android.systemui", UserHandle.USER_OWNER);
212                ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid);
213            }
214        } catch (Exception ex) {
215            Log.e(TAG, "checkIfCallerIsSelfOrForegroundUser: Exception ex=" + ex);
216            ok = false;
217        } finally {
218            Binder.restoreCallingIdentity(ident);
219        }
220        return ok;
221    }
222
223    public static boolean checkCallerAllowManagedProfiles(Context mContext) {
224        if (mContext == null) {
225            return checkCaller();
226        }
227        boolean ok;
228        // Get the caller's user id and if it's a managed profile, get it's parents
229        // id, then clear the calling identity
230        // which will be restored in the finally clause.
231        int callingUser = UserHandle.getCallingUserId();
232        int callingUid = Binder.getCallingUid();
233        long ident = Binder.clearCallingIdentity();
234        try {
235            UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
236            UserInfo ui = um.getProfileParent(callingUser);
237            int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
238            // With calling identity cleared the current user is the foreground user.
239            int foregroundUser = ActivityManager.getCurrentUser();
240            ok = (foregroundUser == callingUser) ||
241                    (foregroundUser == parentUser);
242            if (!ok) {
243                // Always allow SystemUI/System access.
244                int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
245                        "com.android.systemui", UserHandle.USER_OWNER);
246                ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid);
247            }
248        } catch (Exception ex) {
249            Log.e(TAG, "checkCallerAllowManagedProfiles: Exception ex=" + ex);
250            ok = false;
251        } finally {
252            Binder.restoreCallingIdentity(ident);
253        }
254        return ok;
255    }
256
257    /**
258     * Enforce the context has android.Manifest.permission.BLUETOOTH_ADMIN permission. A
259     * {@link SecurityException} would be thrown if neither the calling process or the application
260     * does not have BLUETOOTH_ADMIN permission.
261     *
262     * @param context Context for the permission check.
263     */
264    public static void enforceAdminPermission(ContextWrapper context) {
265        context.enforceCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_ADMIN,
266                "Need BLUETOOTH_ADMIN permission");
267    }
268
269    /**
270     * Converts {@code millisecond} to unit. Each unit is 0.625 millisecond.
271     */
272    public static int millsToUnit(int milliseconds) {
273        return (int) (TimeUnit.MILLISECONDS.toMicros(milliseconds) / MICROS_PER_UNIT);
274    }
275}
276