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