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