RemoteDevices.java revision cc4730be1fcf272355368ca4a2c464d71d3f432d
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.btservice;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothClass;
21import android.bluetooth.BluetoothDevice;
22import android.content.Context;
23import android.content.Intent;
24import android.os.Handler;
25import android.os.Message;
26import android.os.ParcelUuid;
27import android.util.Log;
28
29import com.android.bluetooth.Utils;
30import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
31
32import java.util.ArrayList;
33import java.util.HashMap;
34import java.util.LinkedList;
35
36
37final class RemoteDevices {
38    private static final boolean DBG = false;
39    private static final String TAG = "BluetoothRemoteDevices";
40
41
42    private static BluetoothAdapter mAdapter;
43    private static AdapterService mAdapterService;
44    private static ArrayList<BluetoothDevice> mSdpTracker;
45
46    private Object mObject = new Object();
47
48    private static final int UUID_INTENT_DELAY = 6000;
49    private static final int MESSAGE_UUID_INTENT = 1;
50
51    private HashMap<BluetoothDevice, DeviceProperties> mDevices;
52
53    RemoteDevices(AdapterService service) {
54        mAdapter = BluetoothAdapter.getDefaultAdapter();
55        mAdapterService = service;
56        mSdpTracker = new ArrayList<BluetoothDevice>();
57        mDevices = new HashMap<BluetoothDevice, DeviceProperties>();
58    }
59
60
61    void cleanup() {
62        if (mSdpTracker !=null)
63            mSdpTracker.clear();
64
65        if (mDevices != null)
66            mDevices.clear();
67    }
68
69    public Object Clone() throws CloneNotSupportedException {
70        throw new CloneNotSupportedException();
71    }
72
73    DeviceProperties getDeviceProperties(BluetoothDevice device) {
74        synchronized (mDevices) {
75            return mDevices.get(device);
76        }
77    }
78
79    BluetoothDevice getDevice(byte[] address) {
80        for (BluetoothDevice dev : mDevices.keySet()) {
81            if (dev.getAddress().equals(Utils.getAddressStringFromByte(address))) {
82                return dev;
83            }
84        }
85        return null;
86    }
87
88    DeviceProperties addDeviceProperties(byte[] address) {
89        synchronized (mDevices) {
90            DeviceProperties prop = new DeviceProperties();
91            BluetoothDevice device =
92                    mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
93            prop.mAddress = address;
94            mDevices.put(device, prop);
95            return prop;
96        }
97    }
98
99    class DeviceProperties {
100        private String mName;
101        private byte[] mAddress;
102        private int mBluetoothClass;
103        private short mRssi;
104        private ParcelUuid[] mUuids;
105        private int mDeviceType;
106        private String mAlias;
107        private int mBondState;
108
109        DeviceProperties() {
110            mBondState = BluetoothDevice.BOND_NONE;
111        }
112
113        /**
114         * @return the mName
115         */
116        String getName() {
117            synchronized (mObject) {
118                return mName;
119            }
120        }
121
122        /**
123         * @return the mClass
124         */
125        int getBluetoothClass() {
126            synchronized (mObject) {
127                return mBluetoothClass;
128            }
129        }
130
131        /**
132         * @return the mUuids
133         */
134        ParcelUuid[] getUuids() {
135            synchronized (mObject) {
136                return mUuids;
137            }
138        }
139
140        /**
141         * @return the mAddress
142         */
143        byte[] getAddress() {
144            synchronized (mObject) {
145                return mAddress;
146            }
147        }
148
149        /**
150         * @return mRssi
151         */
152        short getRssi() {
153            synchronized (mObject) {
154                return mRssi;
155            }
156        }
157
158        /**
159         * @return mDeviceType
160         */
161        int getDeviceType() {
162            synchronized (mObject) {
163                return mDeviceType;
164            }
165        }
166
167        /**
168         * @return the mAlias
169         */
170        String getAlias() {
171            synchronized (mObject) {
172                return mAlias;
173            }
174        }
175
176        /**
177         * @param mAlias the mAlias to set
178         */
179        void setAlias(String mAlias) {
180            synchronized (mObject) {
181                mAdapterService.setDevicePropertyNative(mAddress,
182                    AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
183            }
184        }
185
186        /**
187         * @param mBondState the mBondState to set
188         */
189        void setBondState(int mBondState) {
190            synchronized (mObject) {
191                this.mBondState = mBondState;
192                if (mBondState == BluetoothDevice.BOND_NONE)
193                {
194                    /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
195                    cachedBluetoothDevice issued a connect using the local cached copy of uuids,
196                    without waiting for the ACTION_UUID intent.
197                    This was resulting in multiple calls to connect().*/
198                    mUuids = null;
199                }
200            }
201        }
202
203        /**
204         * @return the mBondState
205         */
206        int getBondState() {
207            synchronized (mObject) {
208                return mBondState;
209            }
210        }
211    }
212
213
214    private void sendUuidIntent(BluetoothDevice device) {
215        DeviceProperties prop = getDeviceProperties(device);
216        Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
217        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
218        intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null? null: prop.mUuids);
219        mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);
220
221        //Remove the outstanding UUID request
222        mSdpTracker.remove(device);
223    }
224
225    private void sendDisplayPinIntent(byte[] address, int pin) {
226        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
227        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, getDevice(address));
228        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin);
229        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
230                    BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN);
231        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
232        mAdapterService.sendOrderedBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
233    }
234
235    void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
236        Intent intent;
237        byte[] val;
238        int type;
239        BluetoothDevice bdDevice = getDevice(address);
240        DeviceProperties device;
241        if (bdDevice == null) {
242            device = addDeviceProperties(address);
243            bdDevice = getDevice(address);
244        } else {
245            device = getDeviceProperties(bdDevice);
246        }
247
248        for (int j = 0; j < types.length; j++) {
249            type = types[j];
250            val = values[j];
251            if(val.length <= 0)
252                errorLog("devicePropertyChangedCallback: bdDevice: " + bdDevice + ", value is empty for type: " + type);
253            else {
254                synchronized(mObject) {
255                    switch (type) {
256                        case AbstractionLayer.BT_PROPERTY_BDNAME:
257                            device.mName = new String(val);
258                            intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
259                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
260                            intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
261                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
262                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
263                            debugLog("Remote Device name is: " + device.mName);
264                            break;
265                        case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
266                            if (device.mAlias != null) {
267                                System.arraycopy(val, 0, device.mAlias, 0, val.length);
268                            }
269                            else {
270                                device.mAlias = new String(val);
271                            }
272                            break;
273                        case AbstractionLayer.BT_PROPERTY_BDADDR:
274                            device.mAddress = val;
275                            debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
276                            break;
277                        case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
278                            device.mBluetoothClass =  Utils.byteArrayToInt(val);
279                            intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
280                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
281                            intent.putExtra(BluetoothDevice.EXTRA_CLASS,
282                                    new BluetoothClass(device.mBluetoothClass));
283                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
284                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
285                            debugLog("Remote class is:" + device.mBluetoothClass);
286                            break;
287                        case AbstractionLayer.BT_PROPERTY_UUIDS:
288                            int numUuids = val.length/AbstractionLayer.BT_UUID_SIZE;
289                            device.mUuids = Utils.byteArrayToUuid(val);
290                            sendUuidIntent(bdDevice);
291                            break;
292                        case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
293                            // The device type from hal layer, defined in bluetooth.h,
294                            // matches the type defined in BluetoothDevice.java
295                            device.mDeviceType = Utils.byteArrayToInt(val);
296                            break;
297                        case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
298                            // RSSI from hal is in one byte
299                            device.mRssi = val[0];
300                            break;
301                    }
302                }
303            }
304        }
305    }
306
307    void deviceFoundCallback(byte[] address) {
308        // The device properties are already registered - we can send the intent
309        // now
310        BluetoothDevice device = getDevice(address);
311        debugLog("deviceFoundCallback: Remote Address is:" + device);
312        DeviceProperties deviceProp = getDeviceProperties(device);
313        if (deviceProp == null) {
314            errorLog("Device Properties is null for Device:" + device);
315            return;
316        }
317
318        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
319        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
320        intent.putExtra(BluetoothDevice.EXTRA_CLASS,
321                new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));
322        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
323        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
324
325        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
326    }
327
328    void pinRequestCallback(byte[] address, byte[] name, int cod) {
329        //TODO(BT): Get wakelock and update name and cod
330        BluetoothDevice bdDevice = getDevice(address);
331        if (bdDevice == null) {
332            addDeviceProperties(address);
333        }
334        BluetoothClass btClass = bdDevice.getBluetoothClass();
335        int btDeviceClass = btClass.getDeviceClass();
336        if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD ||
337            btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
338            // Its a keyboard. Follow the HID spec recommendation of creating the
339            // passkey and displaying it to the user. If the keyboard doesn't follow
340            // the spec recommendation, check if the keyboard has a fixed PIN zero
341            // and pair.
342            //TODO: Add sFixedPinZerosAutoPairKeyboard() and maintain list of devices that have fixed pin
343            /*if (mAdapterService.isFixedPinZerosAutoPairKeyboard(address)) {
344                               mAdapterService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
345                               return;
346                     }*/
347            // Generate a variable PIN. This is not truly random but good enough.
348            int pin = (int) Math.floor(Math.random() * 1000000);
349            sendDisplayPinIntent(address, pin);
350            return;
351        }
352        infoLog("pinRequestCallback: " + address + " name:" + name + " cod:" +
353                cod);
354        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
355        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, getDevice(address));
356        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
357                BluetoothDevice.PAIRING_VARIANT_PIN);
358        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
359        mAdapterService.sendOrderedBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
360        return;
361    }
362
363    void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant,
364            int passkey) {
365        //TODO(BT): Get wakelock and update name and cod
366        BluetoothDevice bdDevice = getDevice(address);
367        if (bdDevice == null) {
368            addDeviceProperties(address);
369        }
370
371        infoLog("sspRequestCallback: " + address + " name: " + name + " cod: " +
372                cod + " pairingVariant " + pairingVariant + " passkey: " + passkey);
373        int variant;
374        boolean displayPasskey = false;
375        if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION) {
376            variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION;
377            displayPasskey = true;
378        } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_CONSENT) {
379            variant = BluetoothDevice.PAIRING_VARIANT_CONSENT;
380        } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY) {
381            variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY;
382        } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION) {
383            variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY;
384            displayPasskey = true;
385        } else {
386            errorLog("SSP Pairing variant not present");
387            return;
388        }
389        BluetoothDevice device = getDevice(address);
390        if (device == null) {
391           warnLog("Device is not known for:" + Utils.getAddressStringFromByte(address));
392           addDeviceProperties(address);
393           device = getDevice(address);
394        }
395        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
396        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
397        if (displayPasskey) {
398            intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, passkey);
399        }
400        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
401        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
402        mAdapterService.sendOrderedBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
403    }
404
405    void aclStateChangeCallback(int status, byte[] address, int newState) {
406        BluetoothDevice device = getDevice(address);
407
408        if (device == null) {
409            errorLog("aclStateChangeCallback: Device is NULL");
410            return;
411        }
412
413        Intent intent = null;
414        if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
415            intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
416            debugLog("aclStateChangeCallback: State:Connected to Device:" + device);
417        } else {
418            intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
419            debugLog("aclStateChangeCallback: State:DisConnected to Device:" + device);
420        }
421        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
422        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
423        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
424    }
425
426    void fetchUuids(BluetoothDevice device) {
427        if (mSdpTracker.contains(device)) return;
428        mSdpTracker.add(device);
429
430        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
431        message.obj = device;
432        mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
433
434        //mAdapterService.getDevicePropertyNative(Utils.getBytesFromAddress(device.getAddress()), AbstractionLayer.BT_PROPERTY_UUIDS);
435        mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
436    }
437
438    private final Handler mHandler = new Handler() {
439        @Override
440        public void handleMessage(Message msg) {
441            switch (msg.what) {
442            case MESSAGE_UUID_INTENT:
443                BluetoothDevice device = (BluetoothDevice)msg.obj;
444                if (device != null) {
445                    sendUuidIntent(device);
446                }
447                break;
448            }
449        }
450    };
451
452    private void errorLog(String msg) {
453        Log.e(TAG, msg);
454    }
455
456    private void debugLog(String msg) {
457        if (DBG) Log.d(TAG, msg);
458    }
459
460    private void infoLog(String msg) {
461        if (DBG) Log.i(TAG, msg);
462    }
463
464    private void warnLog(String msg) {
465        Log.w(TAG, msg);
466    }
467
468}
469