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