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