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