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