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 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 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 73 public Object clone() throws CloneNotSupportedException { 74 throw new CloneNotSupportedException(); 75 } 76 77 DeviceProperties getDeviceProperties(BluetoothDevice device) { 78 synchronized (mDevices) { 79 return mDevices.get(device.getAddress()); 80 } 81 } 82 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 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 125 DeviceProperties() { 126 mBondState = BluetoothDevice.BOND_NONE; 127 } 128 129 /** 130 * @return the mName 131 */ 132 String getName() { 133 synchronized (mObject) { 134 return mName; 135 } 136 } 137 138 /** 139 * @return the mClass 140 */ 141 int getBluetoothClass() { 142 synchronized (mObject) { 143 return mBluetoothClass; 144 } 145 } 146 147 /** 148 * @return the mUuids 149 */ 150 ParcelUuid[] getUuids() { 151 synchronized (mObject) { 152 return mUuids; 153 } 154 } 155 156 /** 157 * @return the mAddress 158 */ 159 byte[] getAddress() { 160 synchronized (mObject) { 161 return mAddress; 162 } 163 } 164 165 /** 166 * @return the mDevice 167 */ 168 BluetoothDevice getDevice() { 169 synchronized (mObject) { 170 return mDevice; 171 } 172 } 173 174 /** 175 * @return mRssi 176 */ 177 short getRssi() { 178 synchronized (mObject) { 179 return mRssi; 180 } 181 } 182 183 /** 184 * @return mDeviceType 185 */ 186 int getDeviceType() { 187 synchronized (mObject) { 188 return mDeviceType; 189 } 190 } 191 192 /** 193 * @return the mAlias 194 */ 195 String getAlias() { 196 synchronized (mObject) { 197 return mAlias; 198 } 199 } 200 201 /** 202 * @param mAlias the mAlias to set 203 */ 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 */ 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 */ 236 int getBondState() { 237 synchronized (mObject) { 238 return mBondState; 239 } 240 } 241 242 /** 243 * @param isBondingInitiatedLocally wether bonding is initiated locally 244 */ 245 void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) { 246 synchronized (mObject) { 247 this.isBondingInitiatedLocally = isBondingInitiatedLocally; 248 } 249 } 250 251 /** 252 * @return the isBondingInitiatedLocally 253 */ 254 boolean isBondingInitiatedLocally() { 255 synchronized (mObject) { 256 return isBondingInitiatedLocally; 257 } 258 } 259 } 260 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 */ 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 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 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 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 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 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 470 private void errorLog(String msg) { 471 Log.e(TAG, msg); 472 } 473 474 private void debugLog(String msg) { 475 if (DBG) Log.d(TAG, msg); 476 } 477 478 private void infoLog(String msg) { 479 if (DBG) Log.i(TAG, msg); 480 } 481 482 private void warnLog(String msg) { 483 Log.w(TAG, msg); 484 } 485 486 } 487