RemoteDevices.java revision 619e48d1c82ff9b0ef25dee9ef3c391c41a61a58
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;
33
34
35final class RemoteDevices {
36    private static final boolean DBG = false;
37    private static final String TAG = "BluetoothRemoteDevices";
38
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 HashMap<String, DeviceProperties> mDevices;
49
50    RemoteDevices(AdapterService service) {
51        mAdapter = BluetoothAdapter.getDefaultAdapter();
52        mAdapterService = service;
53        mSdpTracker = new ArrayList<BluetoothDevice>();
54        mDevices = new HashMap<String, DeviceProperties>();
55    }
56
57
58    void cleanup() {
59        if (mSdpTracker !=null)
60            mSdpTracker.clear();
61
62        if (mDevices != null)
63            mDevices.clear();
64    }
65
66    @Override
67    public Object clone() throws CloneNotSupportedException {
68        throw new CloneNotSupportedException();
69    }
70
71    DeviceProperties getDeviceProperties(BluetoothDevice device) {
72        synchronized (mDevices) {
73            return mDevices.get(device.getAddress());
74        }
75    }
76
77    BluetoothDevice getDevice(byte[] address) {
78        DeviceProperties prop = mDevices.get(Utils.getAddressStringFromByte(address));
79        if (prop == null)
80           return null;
81        return prop.getDevice();
82    }
83
84    DeviceProperties addDeviceProperties(byte[] address) {
85        synchronized (mDevices) {
86            DeviceProperties prop = new DeviceProperties();
87            prop.mDevice = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
88            prop.mAddress = address;
89            mDevices.put(Utils.getAddressStringFromByte(address), prop);
90            return prop;
91        }
92    }
93
94    class DeviceProperties {
95        private String mName;
96        private byte[] mAddress;
97        private int mBluetoothClass;
98        private short mRssi;
99        private ParcelUuid[] mUuids;
100        private int mDeviceType;
101        private String mAlias;
102        private int mBondState;
103        private BluetoothDevice mDevice;
104
105        DeviceProperties() {
106            mBondState = BluetoothDevice.BOND_NONE;
107        }
108
109        /**
110         * @return the mName
111         */
112        String getName() {
113            synchronized (mObject) {
114                return mName;
115            }
116        }
117
118        /**
119         * @return the mClass
120         */
121        int getBluetoothClass() {
122            synchronized (mObject) {
123                return mBluetoothClass;
124            }
125        }
126
127        /**
128         * @return the mUuids
129         */
130        ParcelUuid[] getUuids() {
131            synchronized (mObject) {
132                return mUuids;
133            }
134        }
135
136        /**
137         * @return the mAddress
138         */
139        byte[] getAddress() {
140            synchronized (mObject) {
141                return mAddress;
142            }
143        }
144
145        /**
146         * @return the mDevice
147         */
148        BluetoothDevice getDevice() {
149            synchronized (mObject) {
150                return mDevice;
151            }
152        }
153
154        /**
155         * @return mRssi
156         */
157        short getRssi() {
158            synchronized (mObject) {
159                return mRssi;
160            }
161        }
162
163        /**
164         * @return mDeviceType
165         */
166        int getDeviceType() {
167            synchronized (mObject) {
168                return mDeviceType;
169            }
170        }
171
172        /**
173         * @return the mAlias
174         */
175        String getAlias() {
176            synchronized (mObject) {
177                return mAlias;
178            }
179        }
180
181        /**
182         * @param mAlias the mAlias to set
183         */
184        void setAlias(BluetoothDevice device, String mAlias) {
185            synchronized (mObject) {
186                this.mAlias = mAlias;
187                mAdapterService.setDevicePropertyNative(mAddress,
188                    AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
189                Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
190                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
191                intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias);
192                mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
193            }
194        }
195
196        /**
197         * @param mBondState the mBondState to set
198         */
199        void setBondState(int mBondState) {
200            synchronized (mObject) {
201                this.mBondState = mBondState;
202                if (mBondState == BluetoothDevice.BOND_NONE)
203                {
204                    /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
205                    cachedBluetoothDevice issued a connect using the local cached copy of uuids,
206                    without waiting for the ACTION_UUID intent.
207                    This was resulting in multiple calls to connect().*/
208                    mUuids = null;
209                }
210            }
211        }
212
213        /**
214         * @return the mBondState
215         */
216        int getBondState() {
217            synchronized (mObject) {
218                return mBondState;
219            }
220        }
221    }
222
223    private void sendUuidIntent(BluetoothDevice device) {
224        DeviceProperties prop = getDeviceProperties(device);
225        Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
226        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
227        intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null? null: prop.mUuids);
228        mAdapterService.initProfilePriorities(device, prop.mUuids);
229        mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);
230
231        //Remove the outstanding UUID request
232        mSdpTracker.remove(device);
233    }
234
235
236    void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
237        Intent intent;
238        byte[] val;
239        int type;
240        BluetoothDevice bdDevice = getDevice(address);
241        DeviceProperties device;
242        if (bdDevice == null) {
243            device = addDeviceProperties(address);
244            bdDevice = getDevice(address);
245        } else {
246            device = getDeviceProperties(bdDevice);
247        }
248
249        for (int j = 0; j < types.length; j++) {
250            type = types[j];
251            val = values[j];
252            if(val.length <= 0)
253                errorLog("devicePropertyChangedCallback: bdDevice: " + bdDevice
254                        + ", value is empty for type: " + type);
255            else {
256                synchronized(mObject) {
257                    switch (type) {
258                        case AbstractionLayer.BT_PROPERTY_BDNAME:
259                            device.mName = new String(val);
260                            intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
261                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
262                            intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
263                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
264                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
265                            debugLog("Remote Device name is: " + device.mName);
266                            break;
267                        case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
268                            if (device.mAlias != null) {
269                                System.arraycopy(val, 0, device.mAlias, 0, val.length);
270                            }
271                            else {
272                                device.mAlias = new String(val);
273                            }
274                            break;
275                        case AbstractionLayer.BT_PROPERTY_BDADDR:
276                            device.mAddress = val;
277                            debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
278                            break;
279                        case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
280                            device.mBluetoothClass =  Utils.byteArrayToInt(val);
281                            intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
282                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
283                            intent.putExtra(BluetoothDevice.EXTRA_CLASS,
284                                    new BluetoothClass(device.mBluetoothClass));
285                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
286                            mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
287                            debugLog("Remote class is:" + device.mBluetoothClass);
288                            break;
289                        case AbstractionLayer.BT_PROPERTY_UUIDS:
290                            int numUuids = val.length/AbstractionLayer.BT_UUID_SIZE;
291                            device.mUuids = Utils.byteArrayToUuid(val);
292                            if (mAdapterService.getState() == BluetoothAdapter.STATE_ON)
293                                sendUuidIntent(bdDevice);
294                            break;
295                        case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
296                            // The device type from hal layer, defined in bluetooth.h,
297                            // matches the type defined in BluetoothDevice.java
298                            device.mDeviceType = Utils.byteArrayToInt(val);
299                            break;
300                        case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
301                            // RSSI from hal is in one byte
302                            device.mRssi = val[0];
303                            break;
304                    }
305                }
306            }
307        }
308    }
309
310    void deviceFoundCallback(byte[] address) {
311        // The device properties are already registered - we can send the intent
312        // now
313        BluetoothDevice device = getDevice(address);
314        debugLog("deviceFoundCallback: Remote Address is:" + device);
315        DeviceProperties deviceProp = getDeviceProperties(device);
316        if (deviceProp == null) {
317            errorLog("Device Properties is null for Device:" + device);
318            return;
319        }
320
321        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
322        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
323        intent.putExtra(BluetoothDevice.EXTRA_CLASS,
324                new BluetoothClass(deviceProp.mBluetoothClass));
325        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
326        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
327
328        mAdapterService.sendBroadcastMultiplePermissions(intent,
329                new String[] {AdapterService.BLUETOOTH_PERM,
330                        android.Manifest.permission.ACCESS_COARSE_LOCATION});
331    }
332
333    void aclStateChangeCallback(int status, byte[] address, int newState) {
334        BluetoothDevice device = getDevice(address);
335
336        if (device == null) {
337            errorLog("aclStateChangeCallback: Device is NULL");
338            return;
339        }
340        int state = mAdapterService.getState();
341        Log.e(TAG, "state" + state + "newState" + newState);
342
343        DeviceProperties prop = getDeviceProperties(device);
344        if (prop == null) {
345 //         errorLog("aclStateChangeCallback reported unknown device " + Arrays.toString(address));
346        }
347        Intent intent = null;
348        if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
349            if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) {
350                intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
351            } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON) {
352                intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED);
353            }
354            debugLog("aclStateChangeCallback: State:Connected to Device:" + device);
355        } else {
356            if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
357                /*Broadcasting PAIRING_CANCEL intent as well in this case*/
358                intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
359                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
360                mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
361            }
362            if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) {
363                intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
364            } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
365                intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED);
366            }
367            debugLog("aclStateChangeCallback: State:DisConnected to Device:" + device);
368        }
369        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
370        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
371        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
372    }
373
374
375    void fetchUuids(BluetoothDevice device) {
376        if (mSdpTracker.contains(device)) return;
377        mSdpTracker.add(device);
378
379        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
380        message.obj = device;
381        mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
382
383        mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
384    }
385
386    void updateUuids(BluetoothDevice device) {
387        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
388        message.obj = device;
389        mHandler.sendMessage(message);
390    }
391
392    private final Handler mHandler = new Handler() {
393        @Override
394        public void handleMessage(Message msg) {
395            switch (msg.what) {
396            case MESSAGE_UUID_INTENT:
397                BluetoothDevice device = (BluetoothDevice)msg.obj;
398                if (device != null) {
399                    sendUuidIntent(device);
400                }
401                break;
402            }
403        }
404    };
405
406    private void errorLog(String msg) {
407        Log.e(TAG, msg);
408    }
409
410    private void debugLog(String msg) {
411        if (DBG) Log.d(TAG, msg);
412    }
413
414    private void infoLog(String msg) {
415        if (DBG) Log.i(TAG, msg);
416    }
417
418    private void warnLog(String msg) {
419        Log.w(TAG, msg);
420    }
421
422}
423