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