AdapterProperties.java revision 6654f5c903de510a70f9e72cd5ad7837b615d93f
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; 20 21class AdapterProperties { 22 private static final boolean DBG = true; 23 private static final String TAG = "BluetoothAdapterProperties"; 24 25 private static final int BD_ADDR_LEN = 6; // 6 bytes 26 private String mName; 27 private byte[] mAddress; 28 private int mBluetoothClass; 29 private int mScanMode; 30 private int mDiscoverableTimeout; 31 private ParcelUuid[] mUuids; 32 private BluetoothDevice[] mBondedDevices = new BluetoothDevice[0]; 33 34 private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting; 35 private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState; 36 37 38 private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; 39 private int mState = BluetoothAdapter.STATE_OFF; 40 41 private static AdapterProperties sInstance; 42 private BluetoothAdapter mAdapter; 43 private AdapterService mService; 44 private Context mContext; 45 private boolean mDiscovering; 46 private final RemoteDevices mRemoteDevices; 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 private AdapterProperties(AdapterService service, Context context) { 54 mAdapter = BluetoothAdapter.getDefaultAdapter(); 55 mService = service; 56 mContext = context; 57 mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>(); 58 mRemoteDevices = RemoteDevices.getInstance(service, context); 59 } 60 61 static synchronized AdapterProperties getInstance(AdapterService service, Context context) { 62 if (sInstance == null) { 63 sInstance = new AdapterProperties(service, context); 64 } else { 65 sInstance.mService = service; 66 sInstance.mContext = context; 67 //Cleanup needed? 68 } 69 return sInstance; 70 } 71 72 public void init() { 73 mProfileConnectionState.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 this.mState = mState; 184 } 185 } 186 187 /** 188 * @return the mState 189 */ 190 int getState() { 191 synchronized (mObject) { 192 return mState; 193 } 194 } 195 196 /** 197 * @return the mBondedDevices 198 */ 199 BluetoothDevice[] getBondedDevices() { 200 synchronized (mObject) { 201 return mBondedDevices; 202 } 203 } 204 205 int getDiscoverableTimeout() { 206 synchronized (mObject) { 207 return mDiscoverableTimeout; 208 } 209 } 210 211 boolean setDiscoverableTimeout(int timeout) { 212 synchronized (mObject) { 213 return mService.setAdapterPropertyNative( 214 AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT, 215 Utils.intToByteArray(timeout)); 216 } 217 } 218 219 int getProfileConnectionState(int profile) { 220 synchronized (mObject) { 221 Pair<Integer, Integer> p = mProfileConnectionState.get(profile); 222 if (p != null) return p.first; 223 return BluetoothProfile.STATE_DISCONNECTED; 224 } 225 } 226 227 boolean isDiscovering() { 228 synchronized (mObject) { 229 return mDiscovering; 230 } 231 } 232 233 void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) { 234 if (!validateProfileConnectionState(state) || 235 !validateProfileConnectionState(prevState)) { 236 // Previously, an invalid state was broadcast anyway, 237 // with the invalid state converted to -1 in the intent. 238 // Better to log an error and not send an intent with 239 // invalid contents or set mAdapterConnectionState to -1. 240 errorLog("Error in sendConnectionStateChange: " 241 + "prevState " + prevState + " state " + state); 242 return; 243 } 244 245 synchronized (mObject) { 246 updateProfileConnectionState(profile, state, prevState); 247 248 if (updateCountersAndCheckForConnectionStateChange(state, prevState)) { 249 setConnectionState(state); 250 251 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); 252 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 253 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 254 convertToAdapterState(state)); 255 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, 256 convertToAdapterState(prevState)); 257 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 258 mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); 259 debugLog("CONNECTION_STATE_CHANGE: " + device + ": " 260 + prevState + " -> " + state); 261 } 262 } 263 } 264 265 private boolean validateProfileConnectionState(int state) { 266 return (state == BluetoothProfile.STATE_DISCONNECTED || 267 state == BluetoothProfile.STATE_CONNECTING || 268 state == BluetoothProfile.STATE_CONNECTED || 269 state == BluetoothProfile.STATE_DISCONNECTING); 270 } 271 272 273 private int convertToAdapterState(int state) { 274 switch (state) { 275 case BluetoothProfile.STATE_DISCONNECTED: 276 return BluetoothAdapter.STATE_DISCONNECTED; 277 case BluetoothProfile.STATE_DISCONNECTING: 278 return BluetoothAdapter.STATE_DISCONNECTING; 279 case BluetoothProfile.STATE_CONNECTED: 280 return BluetoothAdapter.STATE_CONNECTED; 281 case BluetoothProfile.STATE_CONNECTING: 282 return BluetoothAdapter.STATE_CONNECTING; 283 } 284 Log.e(TAG, "Error in convertToAdapterState"); 285 return -1; 286 } 287 288 private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) { 289 switch (prevState) { 290 case BluetoothProfile.STATE_CONNECTING: 291 mProfilesConnecting--; 292 break; 293 294 case BluetoothProfile.STATE_CONNECTED: 295 mProfilesConnected--; 296 break; 297 298 case BluetoothProfile.STATE_DISCONNECTING: 299 mProfilesDisconnecting--; 300 break; 301 } 302 303 switch (state) { 304 case BluetoothProfile.STATE_CONNECTING: 305 mProfilesConnecting++; 306 return (mProfilesConnected == 0 && mProfilesConnecting == 1); 307 308 case BluetoothProfile.STATE_CONNECTED: 309 mProfilesConnected++; 310 return (mProfilesConnected == 1); 311 312 case BluetoothProfile.STATE_DISCONNECTING: 313 mProfilesDisconnecting++; 314 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1); 315 316 case BluetoothProfile.STATE_DISCONNECTED: 317 return (mProfilesConnected == 0 && mProfilesConnecting == 0); 318 319 default: 320 return true; 321 } 322 } 323 324 private void updateProfileConnectionState(int profile, int newState, int oldState) { 325 // mProfileConnectionState is a hashmap - 326 // <Integer, Pair<Integer, Integer>> 327 // The key is the profile, the value is a pair. first element 328 // is the state and the second element is the number of devices 329 // in that state. 330 int numDev = 1; 331 int newHashState = newState; 332 boolean update = true; 333 334 // The following conditions are considered in this function: 335 // 1. If there is no record of profile and state - update 336 // 2. If a new device's state is current hash state - increment 337 // number of devices in the state. 338 // 3. If a state change has happened to Connected or Connecting 339 // (if current state is not connected), update. 340 // 4. If numDevices is 1 and that device state is being updated, update 341 // 5. If numDevices is > 1 and one of the devices is changing state, 342 // decrement numDevices but maintain oldState if it is Connected or 343 // Connecting 344 Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile); 345 if (stateNumDev != null) { 346 int currHashState = stateNumDev.first; 347 numDev = stateNumDev.second; 348 349 if (newState == currHashState) { 350 numDev ++; 351 } else if (newState == BluetoothProfile.STATE_CONNECTED || 352 (newState == BluetoothProfile.STATE_CONNECTING && 353 currHashState != BluetoothProfile.STATE_CONNECTED)) { 354 numDev = 1; 355 } else if (numDev == 1 && oldState == currHashState) { 356 update = true; 357 } else if (numDev > 1 && oldState == currHashState) { 358 numDev --; 359 360 if (currHashState == BluetoothProfile.STATE_CONNECTED || 361 currHashState == BluetoothProfile.STATE_CONNECTING) { 362 newHashState = currHashState; 363 } 364 } else { 365 update = false; 366 } 367 } 368 369 if (update) { 370 mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState, 371 numDev)); 372 } 373 } 374 375 void adapterPropertyChangedCallback(int[] types, byte[][] values) { 376 Intent intent; 377 int type; 378 byte[] val; 379 for (int i = 0; i < types.length; i++) { 380 val = values[i]; 381 type = types[i]; 382 infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length); 383 synchronized (mObject) { 384 switch (type) { 385 case AbstractionLayer.BT_PROPERTY_BDNAME: 386 mName = new String(val); 387 intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); 388 intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName); 389 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 390 mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); 391 debugLog("Name is: " + mName); 392 break; 393 case AbstractionLayer.BT_PROPERTY_BDADDR: 394 mAddress = val; 395 debugLog("Address is:" + Utils.getAddressStringFromByte(mAddress)); 396 break; 397 case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: 398 mBluetoothClass = Utils.byteArrayToInt(val, 0); 399 debugLog("BT Class:" + mBluetoothClass); 400 break; 401 case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE: 402 int mode = Utils.byteArrayToInt(val, 0); 403 mScanMode = mService.convertScanModeFromHal(mode); 404 intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); 405 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode); 406 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 407 mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); 408 debugLog("Scan Mode:" + mScanMode); 409 break; 410 case AbstractionLayer.BT_PROPERTY_UUIDS: 411 mUuids = Utils.byteArrayToUuid(val); 412 break; 413 case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES: 414 int number = val.length/BD_ADDR_LEN; 415 mBondedDevices = new BluetoothDevice[number]; 416 byte[] addrByte = new byte[BD_ADDR_LEN]; 417 for (int j = 0; j < number; j++) { 418 System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN); 419 mBondedDevices[j] = mRemoteDevices.getDevice(addrByte); 420 DeviceProperties prop = null; 421 if ( mBondedDevices[j] != null ) 422 prop = mRemoteDevices.getDeviceProperties(mBondedDevices[j]); 423 if(mBondedDevices[j] == null || prop == null){ 424 prop = mRemoteDevices.addDeviceProperties(addrByte); 425 mBondedDevices[j] = mRemoteDevices.getDevice(addrByte); 426 } 427 prop.setBondState(BluetoothDevice.BOND_BONDED); 428 debugLog("Bonded Device" + mBondedDevices[j]); 429 } 430 break; 431 case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT: 432 mDiscoverableTimeout = Utils.byteArrayToInt(val, 0); 433 debugLog("Discoverable Timeout:" + mDiscoverableTimeout); 434 break; 435 default: 436 errorLog("Property change not handled in Java land:" + type); 437 } 438 } 439 } 440 } 441 442 void onBluetoothReady() { 443 // When BT is being turned on, all adapter properties will be sent in 1 444 // callback. At this stage, set the scan mode. 445 synchronized (mObject) { 446 if (getState() == BluetoothAdapter.STATE_TURNING_ON && 447 mScanMode == BluetoothAdapter.SCAN_MODE_NONE) { 448 /* mDiscoverableTimeout is part of the 449 adapterPropertyChangedCallback received before 450 onBluetoothReady */ 451 if (mDiscoverableTimeout != 0) 452 setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE); 453 else /* if timeout == never (0) at startup */ 454 setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); 455 /* though not always required, this keeps NV up-to date on first-boot after flash */ 456 setDiscoverableTimeout(mDiscoverableTimeout); 457 } 458 } 459 } 460 461 void onBluetoothDisable() { 462 // When BT disable is invoked, set the scan_mode to NONE 463 // so no incoming connections are possible 464 if (getState() == BluetoothAdapter.STATE_TURNING_OFF) { 465 setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE); 466 } 467 } 468 void discoveryStateChangeCallback(int state) { 469 infoLog("Callback:discoveryStateChangeCallback with state:" + state); 470 synchronized (mObject) { 471 Intent intent; 472 if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) { 473 mDiscovering = false; 474 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 475 mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); 476 } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) { 477 mDiscovering = true; 478 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED); 479 mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); 480 } 481 } 482 } 483 484 private void infoLog(String msg) { 485 Log.i(TAG, msg); 486 } 487 488 private void debugLog(String msg) { 489 if (DBG) Log.d(TAG, msg); 490 } 491 492 private void errorLog(String msg) { 493 Log.e(TAG, msg); 494 } 495} 496