RemoteDevices.java revision fbdf4f223574f48f8f3db83ea88e063a7551f799
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.content.Intent;
23import android.os.Handler;
24import android.os.Message;
25import android.os.ParcelUuid;
26import android.util.Log;
27import com.android.bluetooth.Utils;
28import java.util.ArrayList;
29import java.util.HashMap;
30import java.util.LinkedList;
31import java.util.Queue;
32
33final class RemoteDevices {
34    private static final boolean DBG = false;
35    private static final String TAG = "BluetoothRemoteDevices";
36
37    // Maximum number of device properties to remember
38    private static final int MAX_DEVICE_QUEUE_SIZE = 200;
39
40    private static BluetoothAdapter mAdapter;
41    private static AdapterService mAdapterService;
42    private static ArrayList<BluetoothDevice> mSdpTracker;
43    private Object mObject = new Object();
44
45    private static final int UUID_INTENT_DELAY = 6000;
46    private static final int MESSAGE_UUID_INTENT = 1;
47
48    private static final String PAIRING_REQUEST_PACKAGE = "com.android.settings";
49
50    private HashMap<String, DeviceProperties> mDevices;
51    private Queue<String> mDeviceQueue;
52
53    RemoteDevices(AdapterService service) {
54        mAdapter = BluetoothAdapter.getDefaultAdapter();
55        mAdapterService = service;
56        mSdpTracker = new ArrayList<BluetoothDevice>();
57        mDevices = new HashMap<String, DeviceProperties>();
58        mDeviceQueue = new LinkedList<String>();
59    }
60
61
62    void cleanup() {
63        if (mSdpTracker !=null)
64            mSdpTracker.clear();
65
66        if (mDevices != null)
67            mDevices.clear();
68
69        if (mDeviceQueue != null)
70            mDeviceQueue.clear();
71    }
72
73    @Override
74    public Object clone() throws CloneNotSupportedException {
75        throw new CloneNotSupportedException();
76    }
77
78    DeviceProperties getDeviceProperties(BluetoothDevice device) {
79        synchronized (mDevices) {
80            return mDevices.get(device.getAddress());
81        }
82    }
83
84    BluetoothDevice getDevice(byte[] address) {
85        DeviceProperties prop = mDevices.get(Utils.getAddressStringFromByte(address));
86        if (prop == null)
87           return null;
88        return prop.getDevice();
89    }
90
91    DeviceProperties addDeviceProperties(byte[] address) {
92        synchronized (mDevices) {
93            DeviceProperties prop = new DeviceProperties();
94            prop.mDevice = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
95            prop.mAddress = address;
96            String key = Utils.getAddressStringFromByte(address);
97            DeviceProperties pv = mDevices.put(key, prop);
98
99            if (pv == null) {
100                mDeviceQueue.offer(key);
101                if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) {
102                    String deleteKey = mDeviceQueue.poll();
103                    for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
104                        if (device.getAddress().equals(deleteKey)) return prop;
105                    }
106                    debugLog("Removing device " + deleteKey + " from property map");
107                    mDevices.remove(deleteKey);
108                }
109            }
110            return prop;
111        }
112    }
113
114    class DeviceProperties {
115        private String mName;
116        private byte[] mAddress;
117        private int mBluetoothClass;
118        private short mRssi;
119        private ParcelUuid[] mUuids;
120        private int mDeviceType;
121        private String mAlias;
122        private int mBondState;
123        private BluetoothDevice mDevice;
124        private boolean isBondingInitiatedLocally;
125
126        DeviceProperties() {
127            mBondState = BluetoothDevice.BOND_NONE;
128        }
129
130        /**
131         * @return the mName
132         */
133        String getName() {
134            synchronized (mObject) {
135                return mName;
136            }
137        }
138
139        /**
140         * @return the mClass
141         */
142        int getBluetoothClass() {
143            synchronized (mObject) {
144                return mBluetoothClass;
145            }
146        }
147
148        /**
149         * @return the mUuids
150         */
151        ParcelUuid[] getUuids() {
152            synchronized (mObject) {
153                return mUuids;
154            }
155        }
156
157        /**
158         * @return the mAddress
159         */
160        byte[] getAddress() {
161            synchronized (mObject) {
162                return mAddress;
163            }
164        }
165
166        /**
167         * @return the mDevice
168         */
169        BluetoothDevice getDevice() {
170            synchronized (mObject) {
171                return mDevice;
172            }
173        }
174
175        /**
176         * @return mRssi
177         */
178        short getRssi() {
179            synchronized (mObject) {
180                return mRssi;
181            }
182        }
183
184        /**
185         * @return mDeviceType
186         */
187        int getDeviceType() {
188            synchronized (mObject) {
189                return mDeviceType;
190            }
191        }
192
193        /**
194         * @return the mAlias
195         */
196        String getAlias() {
197            synchronized (mObject) {
198                return mAlias;
199            }
200        }
201
202        /**
203         * @param mAlias the mAlias to set
204         */
205        void setAlias(BluetoothDevice device, String mAlias) {
206            synchronized (mObject) {
207                this.mAlias = mAlias;
208                mAdapterService.setDevicePropertyNative(mAddress,
209                    AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
210                Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
211                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
212                intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias);
213                mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
214            }
215        }
216
217        /**
218         * @param mBondState the mBondState to set
219         */
220        void setBondState(int mBondState) {
221            synchronized (mObject) {
222                this.mBondState = mBondState;
223                if (mBondState == BluetoothDevice.BOND_NONE)
224                {
225                    /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
226                    cachedBluetoothDevice issued a connect using the local cached copy of uuids,
227                    without waiting for the ACTION_UUID intent.
228                    This was resulting in multiple calls to connect().*/
229                    mUuids = null;
230                }
231            }
232        }
233
234        /**
235         * @return the mBondState
236         */
237        int getBondState() {
238            synchronized (mObject) {
239                return mBondState;
240            }
241        }
242
243        /**
244         * @param isBondingInitiatedLocally wether bonding is initiated locally
245         */
246        void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) {
247            synchronized (mObject) {
248                this.isBondingInitiatedLocally = isBondingInitiatedLocally;
249            }
250        }
251
252        /**
253         * @return the isBondingInitiatedLocally
254         */
255        boolean isBondingInitiatedLocally() {
256            synchronized (mObject) {
257                return isBondingInitiatedLocally;
258            }
259        }
260    }
261
262    private void sendUuidIntent(BluetoothDevice device) {
263        DeviceProperties prop = getDeviceProperties(device);
264        Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
265        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
266        intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null ? null : prop.mUuids);
267        mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);
268
269        //Remove the outstanding UUID request
270        mSdpTracker.remove(device);
271    }
272
273  /**
274   * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, we
275   * must add device first before setting it's properties. This is a helper method for doing that.
276   */
277  void setBondingInitiatedLocally(byte[] address) {
278        DeviceProperties properties;
279
280        BluetoothDevice device = getDevice(address);
281        if (device == null) {
282            properties = addDeviceProperties(address);
283        } else {
284            properties = getDeviceProperties(device);
285        }
286
287        properties.setBondingInitiatedLocally(true);
288    }
289
290
291    void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
292        Intent intent;
293        byte[] val;
294        int type;
295        BluetoothDevice bdDevice = getDevice(address);
296        DeviceProperties device;
297        if (bdDevice == null) {
298            device = addDeviceProperties(address);
299            bdDevice = getDevice(address);
300        } else {
301            device = getDeviceProperties(bdDevice);
302        }
303
304        for (int j = 0; j < types.length; j++) {
305            type = types[j];
306            val = values[j];
307            if(val.length <= 0)
308                errorLog("devicePropertyChangedCallback: bdDevice: " + bdDevice
309                        + ", value is empty for type: " + type);
310            else {
311                synchronized(mObject) {
312                    switch (type) {
313                        case AbstractionLayer.BT_PROPERTY_BDNAME:
314                            device.mName = new String(val);
315                            intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
316                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
317                            intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
318                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
319                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
320                            debugLog("Remote Device name is: " + device.mName);
321                            break;
322                        case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
323                            if (device.mAlias != null) {
324                                System.arraycopy(val, 0, device.mAlias, 0, val.length);
325                            }
326                            else {
327                                device.mAlias = new String(val);
328                            }
329                            break;
330                        case AbstractionLayer.BT_PROPERTY_BDADDR:
331                            device.mAddress = val;
332                            debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
333                            break;
334                        case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
335                            device.mBluetoothClass =  Utils.byteArrayToInt(val);
336                            intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
337                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
338                            intent.putExtra(BluetoothDevice.EXTRA_CLASS,
339                                    new BluetoothClass(device.mBluetoothClass));
340                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
341                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
342                            debugLog("Remote class is:" + device.mBluetoothClass);
343                            break;
344                        case AbstractionLayer.BT_PROPERTY_UUIDS:
345                            int numUuids = val.length/AbstractionLayer.BT_UUID_SIZE;
346                            device.mUuids = Utils.byteArrayToUuid(val);
347                            if (mAdapterService.getState() == BluetoothAdapter.STATE_ON)
348                                sendUuidIntent(bdDevice);
349                            break;
350                        case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
351                            // The device type from hal layer, defined in bluetooth.h,
352                            // matches the type defined in BluetoothDevice.java
353                            device.mDeviceType = Utils.byteArrayToInt(val);
354                            break;
355                        case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
356                            // RSSI from hal is in one byte
357                            device.mRssi = val[0];
358                            break;
359                    }
360                }
361            }
362        }
363    }
364
365    void deviceFoundCallback(byte[] address) {
366        // The device properties are already registered - we can send the intent
367        // now
368        BluetoothDevice device = getDevice(address);
369        debugLog("deviceFoundCallback: Remote Address is:" + device);
370        DeviceProperties deviceProp = getDeviceProperties(device);
371        if (deviceProp == null) {
372            errorLog("Device Properties is null for Device:" + device);
373            return;
374        }
375
376        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
377        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
378        intent.putExtra(BluetoothDevice.EXTRA_CLASS,
379                new BluetoothClass(deviceProp.mBluetoothClass));
380        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
381        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
382
383        mAdapterService.sendBroadcastMultiplePermissions(intent,
384                new String[] {AdapterService.BLUETOOTH_PERM,
385                        android.Manifest.permission.ACCESS_COARSE_LOCATION});
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        int state = mAdapterService.getState();
396        Log.e(TAG, "state " + BluetoothAdapter.nameForState(state) + " newState " + newState);
397
398        Intent intent = null;
399        if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
400            if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) {
401                intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
402            } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON) {
403                intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED);
404            }
405            debugLog("aclStateChangeCallback: Connected: " + device);
406        } else {
407            if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
408                // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding.
409                intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
410                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
411                intent.setPackage(PAIRING_REQUEST_PACKAGE);
412                mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
413            }
414            if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) {
415                intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
416            } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
417                intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED);
418            }
419            debugLog("aclStateChangeCallback: Disconnected: " + 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
427    void fetchUuids(BluetoothDevice device) {
428        if (mSdpTracker.contains(device)) return;
429        mSdpTracker.add(device);
430
431        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
432        message.obj = device;
433        mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
434
435        mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
436    }
437
438    void updateUuids(BluetoothDevice device) {
439        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
440        message.obj = device;
441        mHandler.sendMessage(message);
442    }
443
444    private final Handler mHandler = new Handler() {
445        @Override
446        public void handleMessage(Message msg) {
447            switch (msg.what) {
448            case MESSAGE_UUID_INTENT:
449                BluetoothDevice device = (BluetoothDevice)msg.obj;
450                if (device != null) {
451                    sendUuidIntent(device);
452                }
453                break;
454            }
455        }
456    };
457
458    private void errorLog(String msg) {
459        Log.e(TAG, msg);
460    }
461
462    private void debugLog(String msg) {
463        if (DBG) Log.d(TAG, msg);
464    }
465
466    private void infoLog(String msg) {
467        if (DBG) Log.i(TAG, msg);
468    }
469
470    private void warnLog(String msg) {
471        Log.w(TAG, msg);
472    }
473
474}
475