AdapterProperties.java revision 871ab55f8460ecc0cbff29904c312528fb7bbc63
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 17package com.android.bluetooth.btservice; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothDevice; 21import android.bluetooth.BluetoothProfile; 22import android.content.Context; 23import android.content.Intent; 24import android.os.ParcelUuid; 25import android.os.UserHandle; 26import android.util.Log; 27import android.util.Pair; 28 29import com.android.bluetooth.Utils; 30import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; 31 32import java.util.HashMap; 33import java.util.ArrayList; 34import java.util.concurrent.CopyOnWriteArrayList; 35 36class AdapterProperties { 37 private static final boolean DBG = true; 38 private static final boolean VDBG = false; 39 private static final String TAG = "BluetoothAdapterProperties"; 40 41 private static final int BD_ADDR_LEN = 6; // 6 bytes 42 private String mName; 43 private byte[] mAddress; 44 private int mBluetoothClass; 45 private int mScanMode; 46 private int mDiscoverableTimeout; 47 private ParcelUuid[] mUuids; 48 private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices = new CopyOnWriteArrayList<BluetoothDevice>(); 49 50 private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting; 51 private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState; 52 53 54 private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; 55 private int mState = BluetoothAdapter.STATE_OFF; 56 57 private AdapterService mService; 58 private boolean mDiscovering; 59 private RemoteDevices mRemoteDevices; 60 private BluetoothAdapter mAdapter; 61 //TODO - all hw capabilities to be exposed as a class 62 private int mNumOfAdvertisementInstancesSupported; 63 private boolean mRpaOffloadSupported; 64 private int mNumOfOffloadedIrkSupported; 65 private int mNumOfOffloadedScanFilterSupported; 66 private int mOffloadedScanResultStorageBytes; 67 private int mVersSupported; 68 private int mTotNumOfTrackableAdv; 69 private boolean mIsActivityAndEnergyReporting; 70 71 // Lock for all getters and setters. 72 // If finer grained locking is needer, more locks 73 // can be added here. 74 private Object mObject = new Object(); 75 76 public AdapterProperties(AdapterService service) { 77 mService = service; 78 mAdapter = BluetoothAdapter.getDefaultAdapter(); 79 } 80 public void init(RemoteDevices remoteDevices) { 81 if (mProfileConnectionState ==null) { 82 mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>(); 83 } else { 84 mProfileConnectionState.clear(); 85 } 86 mRemoteDevices = remoteDevices; 87 } 88 89 public void cleanup() { 90 mRemoteDevices = null; 91 if (mProfileConnectionState != null) { 92 mProfileConnectionState.clear(); 93 mProfileConnectionState = null; 94 } 95 mService = null; 96 if (!mBondedDevices.isEmpty()) 97 mBondedDevices.clear(); 98 } 99 100 @Override 101 public Object clone() throws CloneNotSupportedException { 102 throw new CloneNotSupportedException(); 103 } 104 105 /** 106 * @return the mName 107 */ 108 String getName() { 109 synchronized (mObject) { 110 return mName; 111 } 112 } 113 114 /** 115 * Set the local adapter property - name 116 * @param name the name to set 117 */ 118 boolean setName(String name) { 119 synchronized (mObject) { 120 return mService.setAdapterPropertyNative( 121 AbstractionLayer.BT_PROPERTY_BDNAME, name.getBytes()); 122 } 123 } 124 125 /** 126 * @return the mClass 127 */ 128 int getBluetoothClass() { 129 synchronized (mObject) { 130 return mBluetoothClass; 131 } 132 } 133 134 /** 135 * @return the mScanMode 136 */ 137 int getScanMode() { 138 synchronized (mObject) { 139 return mScanMode; 140 } 141 } 142 143 /** 144 * Set the local adapter property - scanMode 145 * 146 * @param scanMode the ScanMode to set 147 */ 148 boolean setScanMode(int scanMode) { 149 synchronized (mObject) { 150 return mService.setAdapterPropertyNative( 151 AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE, Utils.intToByteArray(scanMode)); 152 } 153 } 154 155 /** 156 * @return the mUuids 157 */ 158 ParcelUuid[] getUuids() { 159 synchronized (mObject) { 160 return mUuids; 161 } 162 } 163 164 /** 165 * Set local adapter UUIDs. 166 * 167 * @param uuids the uuids to be set. 168 */ 169 boolean setUuids(ParcelUuid[] uuids) { 170 synchronized (mObject) { 171 return mService.setAdapterPropertyNative( 172 AbstractionLayer.BT_PROPERTY_UUIDS, Utils.uuidsToByteArray(uuids)); 173 } 174 } 175 176 /** 177 * @return the mAddress 178 */ 179 byte[] getAddress() { 180 synchronized (mObject) { 181 return mAddress; 182 } 183 } 184 185 /** 186 * @param mConnectionState the mConnectionState to set 187 */ 188 void setConnectionState(int mConnectionState) { 189 synchronized (mObject) { 190 this.mConnectionState = mConnectionState; 191 } 192 } 193 194 /** 195 * @return the mConnectionState 196 */ 197 int getConnectionState() { 198 synchronized (mObject) { 199 return mConnectionState; 200 } 201 } 202 203 /** 204 * @param mState the mState to set 205 */ 206 void setState(int mState) { 207 synchronized (mObject) { 208 debugLog("Setting state to " + mState); 209 this.mState = mState; 210 } 211 } 212 213 /** 214 * @return the mState 215 */ 216 int getState() { 217 /* remove the lock to work around a platform deadlock problem */ 218 /* and also for read access, it is safe to remove the lock to save CPU power */ 219 return mState; 220 } 221 222 /** 223 * @return the mNumOfAdvertisementInstancesSupported 224 */ 225 int getNumOfAdvertisementInstancesSupported() { 226 return mNumOfAdvertisementInstancesSupported; 227 } 228 229 /** 230 * @return the mRpaOffloadSupported 231 */ 232 boolean isRpaOffloadSupported() { 233 return mRpaOffloadSupported; 234 } 235 236 /** 237 * @return the mNumOfOffloadedIrkSupported 238 */ 239 int getNumOfOffloadedIrkSupported() { 240 return mNumOfOffloadedIrkSupported; 241 } 242 243 /** 244 * @return the mNumOfOffloadedScanFilterSupported 245 */ 246 int getNumOfOffloadedScanFilterSupported() { 247 return mNumOfOffloadedScanFilterSupported; 248 } 249 250 /** 251 * @return the mOffloadedScanResultStorageBytes 252 */ 253 int getOffloadedScanResultStorage() { 254 return mOffloadedScanResultStorageBytes; 255 } 256 257 /** 258 * @return tx/rx/idle activity and energy info 259 */ 260 boolean isActivityAndEnergyReportingSupported() { 261 return mIsActivityAndEnergyReporting; 262 } 263 264 /** 265 * @return total number of trackable advertisements 266 */ 267 int getTotalNumOfTrackableAdvertisements() { 268 return mTotNumOfTrackableAdv; 269 } 270 271 /** 272 * @return the mBondedDevices 273 */ 274 BluetoothDevice[] getBondedDevices() { 275 BluetoothDevice[] bondedDeviceList = new BluetoothDevice[0]; 276 synchronized (mObject) { 277 if(mBondedDevices.isEmpty()) 278 return (new BluetoothDevice[0]); 279 280 try { 281 bondedDeviceList = mBondedDevices.toArray(bondedDeviceList); 282 infoLog("getBondedDevices: length="+bondedDeviceList.length); 283 return bondedDeviceList; 284 } catch(ArrayStoreException ee) { 285 errorLog("Error retrieving bonded device array"); 286 return (new BluetoothDevice[0]); 287 } 288 } 289 } 290 // This function shall be invoked from BondStateMachine whenever the bond 291 // state changes. 292 void onBondStateChanged(BluetoothDevice device, int state) 293 { 294 if(device == null) 295 return; 296 try { 297 byte[] addrByte = Utils.getByteAddress(device); 298 DeviceProperties prop = mRemoteDevices.getDeviceProperties(device); 299 if (prop == null) 300 prop = mRemoteDevices.addDeviceProperties(addrByte); 301 prop.setBondState(state); 302 303 if (state == BluetoothDevice.BOND_BONDED) { 304 // add if not already in list 305 if(!mBondedDevices.contains(device)) { 306 debugLog("Adding bonded device:" + device); 307 mBondedDevices.add(device); 308 } 309 } else if (state == BluetoothDevice.BOND_NONE) { 310 // remove device from list 311 if (mBondedDevices.remove(device)) 312 debugLog("Removing bonded device:" + device); 313 else 314 debugLog("Failed to remove device: " + device); 315 } 316 } 317 catch(Exception ee) { 318 Log.e(TAG, "Exception in onBondStateChanged : ", ee); 319 } 320 } 321 322 int getDiscoverableTimeout() { 323 synchronized (mObject) { 324 return mDiscoverableTimeout; 325 } 326 } 327 328 boolean setDiscoverableTimeout(int timeout) { 329 synchronized (mObject) { 330 return mService.setAdapterPropertyNative( 331 AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT, 332 Utils.intToByteArray(timeout)); 333 } 334 } 335 336 int getProfileConnectionState(int profile) { 337 synchronized (mObject) { 338 Pair<Integer, Integer> p = mProfileConnectionState.get(profile); 339 if (p != null) return p.first; 340 return BluetoothProfile.STATE_DISCONNECTED; 341 } 342 } 343 344 boolean isDiscovering() { 345 synchronized (mObject) { 346 return mDiscovering; 347 } 348 } 349 350 void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) { 351 if (!validateProfileConnectionState(state) || 352 !validateProfileConnectionState(prevState)) { 353 // Previously, an invalid state was broadcast anyway, 354 // with the invalid state converted to -1 in the intent. 355 // Better to log an error and not send an intent with 356 // invalid contents or set mAdapterConnectionState to -1. 357 errorLog("Error in sendConnectionStateChange: " 358 + "prevState " + prevState + " state " + state); 359 return; 360 } 361 362 synchronized (mObject) { 363 updateProfileConnectionState(profile, state, prevState); 364 365 if (updateCountersAndCheckForConnectionStateChange(state, prevState)) { 366 setConnectionState(state); 367 368 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); 369 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 370 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 371 convertToAdapterState(state)); 372 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, 373 convertToAdapterState(prevState)); 374 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 375 mService.sendBroadcastAsUser(intent, UserHandle.ALL, 376 mService.BLUETOOTH_PERM); 377 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": " 378 + prevState + " -> " + state); 379 } 380 } 381 } 382 383 private boolean validateProfileConnectionState(int state) { 384 return (state == BluetoothProfile.STATE_DISCONNECTED || 385 state == BluetoothProfile.STATE_CONNECTING || 386 state == BluetoothProfile.STATE_CONNECTED || 387 state == BluetoothProfile.STATE_DISCONNECTING); 388 } 389 390 391 private int convertToAdapterState(int state) { 392 switch (state) { 393 case BluetoothProfile.STATE_DISCONNECTED: 394 return BluetoothAdapter.STATE_DISCONNECTED; 395 case BluetoothProfile.STATE_DISCONNECTING: 396 return BluetoothAdapter.STATE_DISCONNECTING; 397 case BluetoothProfile.STATE_CONNECTED: 398 return BluetoothAdapter.STATE_CONNECTED; 399 case BluetoothProfile.STATE_CONNECTING: 400 return BluetoothAdapter.STATE_CONNECTING; 401 } 402 Log.e(TAG, "Error in convertToAdapterState"); 403 return -1; 404 } 405 406 private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) { 407 switch (prevState) { 408 case BluetoothProfile.STATE_CONNECTING: 409 mProfilesConnecting--; 410 break; 411 412 case BluetoothProfile.STATE_CONNECTED: 413 mProfilesConnected--; 414 break; 415 416 case BluetoothProfile.STATE_DISCONNECTING: 417 mProfilesDisconnecting--; 418 break; 419 } 420 421 switch (state) { 422 case BluetoothProfile.STATE_CONNECTING: 423 mProfilesConnecting++; 424 return (mProfilesConnected == 0 && mProfilesConnecting == 1); 425 426 case BluetoothProfile.STATE_CONNECTED: 427 mProfilesConnected++; 428 return (mProfilesConnected == 1); 429 430 case BluetoothProfile.STATE_DISCONNECTING: 431 mProfilesDisconnecting++; 432 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1); 433 434 case BluetoothProfile.STATE_DISCONNECTED: 435 return (mProfilesConnected == 0 && mProfilesConnecting == 0); 436 437 default: 438 return true; 439 } 440 } 441 442 private void updateProfileConnectionState(int profile, int newState, int oldState) { 443 // mProfileConnectionState is a hashmap - 444 // <Integer, Pair<Integer, Integer>> 445 // The key is the profile, the value is a pair. first element 446 // is the state and the second element is the number of devices 447 // in that state. 448 int numDev = 1; 449 int newHashState = newState; 450 boolean update = true; 451 452 // The following conditions are considered in this function: 453 // 1. If there is no record of profile and state - update 454 // 2. If a new device's state is current hash state - increment 455 // number of devices in the state. 456 // 3. If a state change has happened to Connected or Connecting 457 // (if current state is not connected), update. 458 // 4. If numDevices is 1 and that device state is being updated, update 459 // 5. If numDevices is > 1 and one of the devices is changing state, 460 // decrement numDevices but maintain oldState if it is Connected or 461 // Connecting 462 Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile); 463 if (stateNumDev != null) { 464 int currHashState = stateNumDev.first; 465 numDev = stateNumDev.second; 466 467 if (newState == currHashState) { 468 numDev ++; 469 } else if (newState == BluetoothProfile.STATE_CONNECTED || 470 (newState == BluetoothProfile.STATE_CONNECTING && 471 currHashState != BluetoothProfile.STATE_CONNECTED)) { 472 numDev = 1; 473 } else if (numDev == 1 && oldState == currHashState) { 474 update = true; 475 } else if (numDev > 1 && oldState == currHashState) { 476 numDev --; 477 478 if (currHashState == BluetoothProfile.STATE_CONNECTED || 479 currHashState == BluetoothProfile.STATE_CONNECTING) { 480 newHashState = currHashState; 481 } 482 } else { 483 update = false; 484 } 485 } 486 487 if (update) { 488 mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState, 489 numDev)); 490 } 491 } 492 493 void adapterPropertyChangedCallback(int[] types, byte[][] values) { 494 Intent intent; 495 int type; 496 byte[] val; 497 for (int i = 0; i < types.length; i++) { 498 val = values[i]; 499 type = types[i]; 500 infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length); 501 synchronized (mObject) { 502 switch (type) { 503 case AbstractionLayer.BT_PROPERTY_BDNAME: 504 mName = new String(val); 505 intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); 506 intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName); 507 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 508 mService.sendBroadcastAsUser(intent, UserHandle.ALL, 509 mService.BLUETOOTH_PERM); 510 debugLog("Name is: " + mName); 511 break; 512 case AbstractionLayer.BT_PROPERTY_BDADDR: 513 mAddress = val; 514 debugLog("Address is:" + Utils.getAddressStringFromByte(mAddress)); 515 break; 516 case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: 517 mBluetoothClass = Utils.byteArrayToInt(val, 0); 518 debugLog("BT Class:" + mBluetoothClass); 519 break; 520 case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE: 521 int mode = Utils.byteArrayToInt(val, 0); 522 mScanMode = mService.convertScanModeFromHal(mode); 523 intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); 524 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode); 525 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 526 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); 527 debugLog("Scan Mode:" + mScanMode); 528 if (mBluetoothDisabling) { 529 mBluetoothDisabling=false; 530 mService.startBluetoothDisable(); 531 } 532 break; 533 case AbstractionLayer.BT_PROPERTY_UUIDS: 534 mUuids = Utils.byteArrayToUuid(val); 535 break; 536 case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES: 537 int number = val.length/BD_ADDR_LEN; 538 byte[] addrByte = new byte[BD_ADDR_LEN]; 539 for (int j = 0; j < number; j++) { 540 System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN); 541 onBondStateChanged(mAdapter.getRemoteDevice( 542 Utils.getAddressStringFromByte(addrByte)), 543 BluetoothDevice.BOND_BONDED); 544 } 545 break; 546 case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT: 547 mDiscoverableTimeout = Utils.byteArrayToInt(val, 0); 548 debugLog("Discoverable Timeout:" + mDiscoverableTimeout); 549 break; 550 551 case AbstractionLayer.BT_PROPERTY_LOCAL_LE_FEATURES: 552 updateFeatureSupport(val); 553 break; 554 555 default: 556 errorLog("Property change not handled in Java land:" + type); 557 } 558 } 559 } 560 } 561 562 void updateFeatureSupport(byte[] val) { 563 mVersSupported = ((0xFF & ((int)val[1])) << 8) 564 + (0xFF & ((int)val[0])); 565 mNumOfAdvertisementInstancesSupported = (0xFF & ((int)val[3])); 566 mRpaOffloadSupported = ((0xFF & ((int)val[4]))!= 0); 567 mNumOfOffloadedIrkSupported = (0xFF & ((int)val[5])); 568 mNumOfOffloadedScanFilterSupported = (0xFF & ((int)val[6])); 569 mIsActivityAndEnergyReporting = ((0xFF & ((int)val[7])) != 0); 570 mOffloadedScanResultStorageBytes = ((0xFF & ((int)val[9])) << 8) 571 + (0xFF & ((int)val[8])); 572 mTotNumOfTrackableAdv = ((0xFF & ((int)val[11])) << 8) 573 + (0xFF & ((int)val[10])); 574 575 Log.d(TAG, "BT_PROPERTY_LOCAL_LE_FEATURES: update from BT controller" 576 + " mNumOfAdvertisementInstancesSupported = " 577 + mNumOfAdvertisementInstancesSupported 578 + " mRpaOffloadSupported = " + mRpaOffloadSupported 579 + " mNumOfOffloadedIrkSupported = " 580 + mNumOfOffloadedIrkSupported 581 + " mNumOfOffloadedScanFilterSupported = " 582 + mNumOfOffloadedScanFilterSupported 583 + " mOffloadedScanResultStorageBytes= " 584 + mOffloadedScanResultStorageBytes 585 + " mIsActivityAndEnergyReporting = " 586 + mIsActivityAndEnergyReporting 587 +" mVersSupported = " 588 + mVersSupported 589 + " mTotNumOfTrackableAdv = " 590 + mTotNumOfTrackableAdv); 591 } 592 593 void onBluetoothReady() { 594 Log.d(TAG, "ScanMode = " + mScanMode ); 595 Log.d(TAG, "State = " + getState() ); 596 597 // When BT is being turned on, all adapter properties will be sent in 1 598 // callback. At this stage, set the scan mode. 599 synchronized (mObject) { 600 if (getState() == BluetoothAdapter.STATE_TURNING_ON && 601 mScanMode == BluetoothAdapter.SCAN_MODE_NONE) { 602 /* mDiscoverableTimeout is part of the 603 adapterPropertyChangedCallback received before 604 onBluetoothReady */ 605 if (mDiscoverableTimeout != 0) 606 setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE); 607 else /* if timeout == never (0) at startup */ 608 setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); 609 /* though not always required, this keeps NV up-to date on first-boot after flash */ 610 setDiscoverableTimeout(mDiscoverableTimeout); 611 } 612 } 613 } 614 615 private boolean mBluetoothDisabling = false; 616 617 void onBleDisable() { 618 // Sequence BLE_ON to STATE_OFF - that is _complete_ OFF state. 619 // When BT disable is invoked, set the scan_mode to NONE 620 // so no incoming connections are possible 621 debugLog("onBleDisable"); 622 if (getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF) { 623 setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE); 624 } 625 } 626 627 void onBluetoothDisable() { 628 // From STATE_ON to BLE_ON 629 // When BT disable is invoked, set the scan_mode to NONE 630 // so no incoming connections are possible 631 632 //Set flag to indicate we are disabling. When property change of scan mode done 633 //continue with disable sequence 634 debugLog("onBluetoothDisable()"); 635 mBluetoothDisabling = true; 636 if (getState() == BluetoothAdapter.STATE_TURNING_OFF) { 637 setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE); 638 } 639 } 640 641 void discoveryStateChangeCallback(int state) { 642 infoLog("Callback:discoveryStateChangeCallback with state:" + state); 643 synchronized (mObject) { 644 Intent intent; 645 if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) { 646 mDiscovering = false; 647 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 648 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); 649 } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) { 650 mDiscovering = true; 651 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED); 652 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); 653 } 654 } 655 } 656 657 private void infoLog(String msg) { 658 if (VDBG) Log.i(TAG, msg); 659 } 660 661 private void debugLog(String msg) { 662 if (DBG) Log.d(TAG, msg); 663 } 664 665 private void errorLog(String msg) { 666 Log.e(TAG, msg); 667 } 668} 669