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