RemoteDevices.java revision fd1da115cbf09b7dd9bca3c7d3a4fb816a835dc5
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 = false;
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        if (mSdpTracker !=null)
51            mSdpTracker.clear();
52
53        if (mDevices != null)
54            mDevices.clear();
55    }
56
57    public Object Clone() throws CloneNotSupportedException {
58        throw new CloneNotSupportedException();
59    }
60
61    DeviceProperties getDeviceProperties(BluetoothDevice device) {
62        synchronized (mDevices) {
63            return mDevices.get(device);
64        }
65    }
66
67    BluetoothDevice getDevice(byte[] address) {
68        for (BluetoothDevice dev : mDevices.keySet()) {
69            if (dev.getAddress().equals(Utils.getAddressStringFromByte(address))) {
70                return dev;
71            }
72        }
73        return null;
74    }
75
76    DeviceProperties addDeviceProperties(byte[] address) {
77        synchronized (mDevices) {
78            DeviceProperties prop = new DeviceProperties();
79            BluetoothDevice device =
80                    mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
81            prop.mAddress = 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            if(val.length <= 0)
240                errorLog("devicePropertyChangedCallback: bdDevice: " + bdDevice + ", value is empty for type: " + type);
241            else {
242                synchronized(mObject) {
243                    switch (type) {
244                        case AbstractionLayer.BT_PROPERTY_BDNAME:
245                            device.mName = new String(val);
246                            intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
247                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
248                            intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
249                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
250                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
251                            debugLog("Remote Device name is: " + device.mName);
252                            break;
253                        case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
254                            if (device.mAlias != null) {
255                                System.arraycopy(val, 0, device.mAlias, 0, val.length);
256                            }
257                            else {
258                                device.mAlias = new String(val);
259                            }
260                            break;
261                        case AbstractionLayer.BT_PROPERTY_BDADDR:
262                            device.mAddress = val;
263                            debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
264                            break;
265                        case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
266                            device.mBluetoothClass =  Utils.byteArrayToInt(val);
267                            intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
268                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
269                            intent.putExtra(BluetoothDevice.EXTRA_CLASS,
270                                    new BluetoothClass(device.mBluetoothClass));
271                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
272                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
273                            debugLog("Remote class is:" + device.mBluetoothClass);
274                            break;
275                        case AbstractionLayer.BT_PROPERTY_UUIDS:
276                            int numUuids = val.length/AbstractionLayer.BT_UUID_SIZE;
277                            device.mUuids = Utils.byteArrayToUuid(val);
278                            sendUuidIntent(bdDevice);
279                            break;
280                        case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
281                            device.mDeviceType = Utils.byteArrayToInt(val);
282                            break;
283                        case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
284                            device.mRssi = Utils.byteArrayToShort(val);
285                            break;
286                    }
287                }
288            }
289        }
290    }
291
292    void deviceFoundCallback(byte[] address) {
293        // The device properties are already registered - we can send the intent
294        // now
295        BluetoothDevice device = getDevice(address);
296        debugLog("deviceFoundCallback: Remote Address is:" + device);
297        DeviceProperties deviceProp = getDeviceProperties(device);
298        if (deviceProp == null) {
299            errorLog("Device Properties is null for Device:" + device);
300            return;
301        }
302
303        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
304        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
305        intent.putExtra(BluetoothDevice.EXTRA_CLASS,
306                new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));
307        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
308        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
309
310        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
311    }
312
313    void pinRequestCallback(byte[] address, byte[] name, int cod) {
314        //TODO(BT): Get wakelock and update name and cod
315        BluetoothDevice bdDevice = getDevice(address);
316        if (bdDevice == null) {
317            addDeviceProperties(address);
318        }
319        BluetoothClass btClass = bdDevice.getBluetoothClass();
320        int btDeviceClass = btClass.getDeviceClass();
321        if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD ||
322            btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
323            // Its a keyboard. Follow the HID spec recommendation of creating the
324            // passkey and displaying it to the user. If the keyboard doesn't follow
325            // the spec recommendation, check if the keyboard has a fixed PIN zero
326            // and pair.
327            //TODO: Add sFixedPinZerosAutoPairKeyboard() and maintain list of devices that have fixed pin
328            /*if (mAdapterService.isFixedPinZerosAutoPairKeyboard(address)) {
329                               mAdapterService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
330                               return;
331                     }*/
332            // Generate a variable PIN. This is not truly random but good enough.
333            int pin = (int) Math.floor(Math.random() * 1000000);
334            sendDisplayPinIntent(address, pin);
335            return;
336        }
337        infoLog("pinRequestCallback: " + address + " name:" + name + " cod:" +
338                cod);
339        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
340        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, getDevice(address));
341        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
342                BluetoothDevice.PAIRING_VARIANT_PIN);
343        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
344        return;
345    }
346
347    void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant,
348            int passkey) {
349        //TODO(BT): Get wakelock and update name and cod
350        BluetoothDevice bdDevice = getDevice(address);
351        if (bdDevice == null) {
352            addDeviceProperties(address);
353        }
354
355        infoLog("sspRequestCallback: " + address + " name: " + name + " cod: " +
356                cod + " pairingVariant " + pairingVariant + " passkey: " + passkey);
357        int variant;
358        boolean displayPasskey = false;
359        if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION) {
360            variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION;
361            displayPasskey = true;
362        } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_CONSENT) {
363            variant = BluetoothDevice.PAIRING_VARIANT_CONSENT;
364        } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY) {
365            variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY;
366        } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION) {
367            variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY;
368	    displayPasskey = true;
369        } else {
370            errorLog("SSP Pairing variant not present");
371            return;
372        }
373        BluetoothDevice device = getDevice(address);
374        if (device == null) {
375           warnLog("Device is not known for:" + Utils.getAddressStringFromByte(address));
376           addDeviceProperties(address);
377           device = getDevice(address);
378        }
379        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
380        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
381        if (displayPasskey) {
382            intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, passkey);
383        }
384        intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
385        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
386    }
387
388    void aclStateChangeCallback(int status, byte[] address, int newState) {
389        BluetoothDevice device = getDevice(address);
390
391        if (device == null) {
392            errorLog("aclStateChangeCallback: Device is NULL");
393            return;
394        }
395
396        Intent intent = null;
397        if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
398            intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
399            debugLog("aclStateChangeCallback: State:Connected to Device:" + device);
400        } else {
401            intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
402            debugLog("aclStateChangeCallback: State:DisConnected to Device:" + device);
403        }
404        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
405        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
406        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
407    }
408
409    void fetchUuids(BluetoothDevice device) {
410        if (mSdpTracker.contains(device)) return;
411        mSdpTracker.add(device);
412
413        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
414        message.obj = device;
415        mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
416
417        //mAdapterService.getDevicePropertyNative(Utils.getBytesFromAddress(device.getAddress()), AbstractionLayer.BT_PROPERTY_UUIDS);
418        mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
419    }
420
421    private final Handler mHandler = new Handler() {
422        @Override
423        public void handleMessage(Message msg) {
424            switch (msg.what) {
425            case MESSAGE_UUID_INTENT:
426                BluetoothDevice device = (BluetoothDevice)msg.obj;
427                if (device != null) {
428                    sendUuidIntent(device);
429                }
430                break;
431            }
432        }
433    };
434
435    private void errorLog(String msg) {
436        Log.e(TAG, msg);
437    }
438
439    private void debugLog(String msg) {
440        if (DBG) Log.d(TAG, msg);
441    }
442
443    private void infoLog(String msg) {
444        if (DBG) Log.i(TAG, msg);
445    }
446
447    private void warnLog(String msg) {
448        Log.w(TAG, msg);
449    }
450
451}
452