RemoteDevices.java revision c4b1ccb46884a0e23e5411f2bbb2da72fcb267ca
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;
27
28import com.android.bluetooth.Utils;
29
30import java.util.concurrent.atomic.AtomicInteger;
31import java.util.ArrayList;
32import java.util.HashMap;
33import java.util.LinkedList;
34import java.util.Queue;
35
36final class RemoteDevices {
37    private static final boolean DBG = false;
38    private static final String TAG = "BluetoothRemoteDevices";
39
40    // Maximum number of device properties to remember
41    private static final int MAX_DEVICE_QUEUE_SIZE = 200;
42
43    private static BluetoothAdapter mAdapter;
44    private static AdapterService mAdapterService;
45    private static ArrayList<BluetoothDevice> mSdpTracker;
46    private Object mObject = new Object();
47
48    private static final int UUID_INTENT_DELAY = 6000;
49    private static final int MESSAGE_UUID_INTENT = 1;
50
51    private HashMap<String, DeviceProperties> mDevices;
52    private Queue<String> mDeviceQueue;
53
54    RemoteDevices(AdapterService service) {
55        mAdapter = BluetoothAdapter.getDefaultAdapter();
56        mAdapterService = service;
57        mSdpTracker = new ArrayList<BluetoothDevice>();
58        mDevices = new HashMap<String, DeviceProperties>();
59        mDeviceQueue = new LinkedList<String>();
60    }
61
62
63    void cleanup() {
64        if (mSdpTracker !=null)
65            mSdpTracker.clear();
66
67        if (mDevices != null)
68            mDevices.clear();
69
70        if (mDeviceQueue != null)
71            mDeviceQueue.clear();
72    }
73
74    @Override
75    public Object clone() throws CloneNotSupportedException {
76        throw new CloneNotSupportedException();
77    }
78
79    DeviceProperties getDeviceProperties(BluetoothDevice device) {
80        synchronized (mDevices) {
81            return mDevices.get(device.getAddress());
82        }
83    }
84
85    BluetoothDevice getDevice(byte[] address) {
86        DeviceProperties prop = mDevices.get(Utils.getAddressStringFromByte(address));
87        if (prop == null)
88           return null;
89        return prop.getDevice();
90    }
91
92    DeviceProperties addDeviceProperties(byte[] address) {
93        synchronized (mDevices) {
94            DeviceProperties prop = new DeviceProperties();
95            prop.mDevice = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
96            prop.mAddress = address;
97            String key = Utils.getAddressStringFromByte(address);
98            DeviceProperties pv = mDevices.put(key, prop);
99
100            if (pv == null) {
101                mDeviceQueue.offer(key);
102                if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) {
103                    String deleteKey = mDeviceQueue.poll();
104                    for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
105                        if (device.getAddress().equals(deleteKey)) return prop;
106                    }
107                    debugLog("Removing device " + deleteKey + " from property map");
108                    mDevices.remove(deleteKey);
109                }
110            }
111            return prop;
112        }
113    }
114
115    class DeviceProperties {
116        private String mName;
117        private byte[] mAddress;
118        private int mBluetoothClass;
119        private short mRssi;
120        private ParcelUuid[] mUuids;
121        private int mDeviceType;
122        private String mAlias;
123        private int mBondState;
124        private BluetoothDevice mDevice;
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    private void sendUuidIntent(BluetoothDevice device) {
245        DeviceProperties prop = getDeviceProperties(device);
246        Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
247        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
248        intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null? null: prop.mUuids);
249        mAdapterService.initProfilePriorities(device, prop.mUuids);
250        mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);
251
252        //Remove the outstanding UUID request
253        mSdpTracker.remove(device);
254    }
255
256
257    void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
258        Intent intent;
259        byte[] val;
260        int type;
261        BluetoothDevice bdDevice = getDevice(address);
262        DeviceProperties device;
263        if (bdDevice == null) {
264            debugLog("Added new device property");
265            device = addDeviceProperties(address);
266            bdDevice = getDevice(address);
267        } else {
268            device = getDeviceProperties(bdDevice);
269        }
270
271        if (types.length <= 0) {
272            errorLog("No properties to update");
273            return;
274        }
275
276        for (int j = 0; j < types.length; j++) {
277            type = types[j];
278            val = values[j];
279            if (val.length <= 0)
280                errorLog("devicePropertyChangedCallback: bdDevice: " + bdDevice
281                        + ", value is empty for type: " + type);
282            else {
283                synchronized(mObject) {
284                    debugLog("Property type: " + type);
285                    switch (type) {
286                        case AbstractionLayer.BT_PROPERTY_BDNAME:
287                            device.mName = new String(val);
288                            intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
289                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
290                            intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
291                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
292                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
293                            debugLog("Remote Device name is: " + device.mName);
294                            break;
295                        case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
296                            if (device.mAlias != null) {
297                                System.arraycopy(val, 0, device.mAlias, 0, val.length);
298                            }
299                            else {
300                                device.mAlias = new String(val);
301                            }
302                            break;
303                        case AbstractionLayer.BT_PROPERTY_BDADDR:
304                            device.mAddress = val;
305                            debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
306                            break;
307                        case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
308                            device.mBluetoothClass =  Utils.byteArrayToInt(val);
309                            intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
310                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
311                            intent.putExtra(BluetoothDevice.EXTRA_CLASS,
312                                    new BluetoothClass(device.mBluetoothClass));
313                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
314                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
315                            debugLog("Remote class is:" + device.mBluetoothClass);
316                            break;
317                        case AbstractionLayer.BT_PROPERTY_UUIDS:
318                            int numUuids = val.length/AbstractionLayer.BT_UUID_SIZE;
319                            device.mUuids = Utils.byteArrayToUuid(val);
320                            if (mAdapterService.getState() == BluetoothAdapter.STATE_ON)
321                                sendUuidIntent(bdDevice);
322                            break;
323                        case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
324                            // The device type from hal layer, defined in bluetooth.h,
325                            // matches the type defined in BluetoothDevice.java
326                            device.mDeviceType = Utils.byteArrayToInt(val);
327                            break;
328                        case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
329                            // RSSI from hal is in one byte
330                            device.mRssi = val[0];
331                            break;
332                    }
333                }
334            }
335        }
336    }
337
338    void deviceFoundCallback(byte[] address) {
339        // The device properties are already registered - we can send the intent
340        // now
341        BluetoothDevice device = getDevice(address);
342        debugLog("deviceFoundCallback: Remote Address is:" + device);
343        DeviceProperties deviceProp = getDeviceProperties(device);
344        if (deviceProp == null) {
345            errorLog("Device Properties is null for Device:" + device);
346            return;
347        }
348
349        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
350        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
351        intent.putExtra(BluetoothDevice.EXTRA_CLASS,
352                new BluetoothClass(deviceProp.mBluetoothClass));
353        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
354        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
355
356        mAdapterService.sendBroadcastMultiplePermissions(intent,
357                new String[] {AdapterService.BLUETOOTH_PERM,
358                        android.Manifest.permission.ACCESS_COARSE_LOCATION});
359    }
360
361    void aclStateChangeCallback(int status, byte[] address, int newState) {
362        BluetoothDevice device = getDevice(address);
363
364        if (device == null) {
365            errorLog("aclStateChangeCallback: Device is NULL");
366            return;
367        }
368        int state = mAdapterService.getState();
369        Log.e(TAG, "state" + state + "newState" + newState);
370
371        DeviceProperties prop = getDeviceProperties(device);
372        if (prop == null) {
373 //         errorLog("aclStateChangeCallback reported unknown device " + Arrays.toString(address));
374        }
375        Intent intent = null;
376        if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
377            if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) {
378                intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
379            } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON) {
380                intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED);
381            }
382            debugLog("aclStateChangeCallback: State:Connected to Device:" + device);
383        } else {
384            if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
385                /*Broadcasting PAIRING_CANCEL intent as well in this case*/
386                intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
387                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
388                mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
389            }
390            if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) {
391                intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
392            } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
393                intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED);
394            }
395            debugLog("aclStateChangeCallback: State:DisConnected to Device:" + device);
396        }
397        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
398        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
399        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
400    }
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.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
412    }
413
414    void updateUuids(BluetoothDevice device) {
415        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
416        message.obj = device;
417        mHandler.sendMessage(message);
418    }
419
420    private final Handler mHandler = new Handler() {
421        @Override
422        public void handleMessage(Message msg) {
423            switch (msg.what) {
424            case MESSAGE_UUID_INTENT:
425                BluetoothDevice device = (BluetoothDevice)msg.obj;
426                if (device != null) {
427                    sendUuidIntent(device);
428                }
429                break;
430            }
431        }
432    };
433
434    private void errorLog(String msg) {
435        Log.e(TAG, msg);
436    }
437
438    private void debugLog(String msg) {
439        if (DBG) Log.d(TAG, msg);
440    }
441
442    private void infoLog(String msg) {
443        if (DBG) Log.i(TAG, msg);
444    }
445
446    private void warnLog(String msg) {
447        Log.w(TAG, msg);
448    }
449
450}
451