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