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