1/* 2 * Copyright (C) 2017 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.car; 18 19import android.annotation.Nullable; 20import android.bluetooth.BluetoothDevice; 21import android.car.CarBluetoothManager; 22import android.util.Log; 23 24import java.util.List; 25import java.util.ArrayList; 26 27import android.bluetooth.BluetoothProfile; 28 29import com.android.internal.annotations.VisibleForTesting; 30 31/** 32 * BluetoothDevicesInfo contains all the information pertinent to connection on a Bluetooth Profile. 33 * It holds 34 * 1. a list of devices {@link #mDeviceInfoList} that has previously paired and connected on this 35 * profile. 36 * 2. a Connection Info object {@link #mConnectionInfo} that has following book keeping information: 37 * a) profile 38 * b) Current Connection status 39 * c) If there are any devices available for connection 40 * d) Index of the Device list that a connection is being tried upon currently. 41 * e) Number of devices that have been previously paired and connected on this profile. 42 * f) How many retry attempts have been made 43 * 44 * This is used by the {@link BluetoothDeviceConnectionPolicy} to find the device to attempt 45 * a connection on for a profile. The policy also updates this object with the connection 46 * results. 47 */ 48public class BluetoothDevicesInfo { 49 50 private static final String TAG = "CarBluetoothDevicesInfo"; 51 private static final boolean DBG = Utils.DBG; 52 private final int DEVICE_NOT_FOUND = -1; 53 private final int DEVICE_PRIORITY_UNDEFINED = -1; 54 // The device list and the connection state information together have all the information 55 // that is required to know which device(s) to connect to, when we need to connect/ 56 private List<DeviceInfo> mDeviceInfoList; 57 private ConnectionInfo mConnectionInfo; 58 59 /** 60 * This class holds on to information regarding this bluetooth profile's connection state. 61 */ 62 private class ConnectionInfo { 63 // which bluetooth profile this Device Info is for 64 private int mProfile; 65 // are there any devices available to connect. It is false either if 66 // 1. no device has been paired to connect on this profile 67 // 2. all paired devices have been tried to connect to, but unsuccessful (not in range etc) 68 private boolean mDeviceAvailableToConnect; 69 // index of device in the mDeviceInfoList that the next connection attempt should be made 70 private int mDeviceIndex; 71 // Connection Retry counter 72 private int mRetryAttempt; 73 // Current number of active connections on this profile 74 private int mNumActiveConnections; 75 // number of concurrent active connections supported. 76 private int mNumConnectionsSupported; 77 78 public ConnectionInfo(int profile) { 79 // Default the number of concurrent active connections supported to 1. 80 this(profile, 1); 81 } 82 83 public ConnectionInfo(int profile, int numConnectionsSupported) { 84 mProfile = profile; 85 mNumConnectionsSupported = numConnectionsSupported; 86 initConnectionInfo(); 87 } 88 89 private void initConnectionInfo() { 90 mDeviceAvailableToConnect = true; 91 mDeviceIndex = 0; 92 mRetryAttempt = 0; 93 mNumActiveConnections = 0; 94 } 95 } 96 97 /** 98 * This class holds information about the list of devices that can connect (have connected in 99 * the past) and their current connection state. 100 */ 101 public class DeviceInfo { 102 103 private BluetoothDevice mBluetoothDevice; 104 private int mConnectionState; 105 private int mDevicePriority; 106 107 public DeviceInfo(BluetoothDevice device, int state) { 108 mBluetoothDevice = device; 109 mConnectionState = state; 110 mDevicePriority = DEVICE_PRIORITY_UNDEFINED; 111 } 112 113 public void setConnectionState(int state) { 114 mConnectionState = state; 115 } 116 117 public int getConnectionState() { 118 return mConnectionState; 119 } 120 121 public BluetoothDevice getBluetoothDevice() { 122 return mBluetoothDevice; 123 } 124 125 public void setBluetoothDevicePriority(int priority) { 126 mDevicePriority = priority; 127 } 128 129 public int getBluetoothDevicePriority() { 130 return mDevicePriority; 131 } 132 } 133 134 public BluetoothDevicesInfo(int profile) { 135 mDeviceInfoList = new ArrayList<>(); 136 mConnectionInfo = new ConnectionInfo(profile); 137 } 138 139 public BluetoothDevicesInfo(int profile, int numConnectionsSupported) { 140 mDeviceInfoList = new ArrayList<>(); 141 mConnectionInfo = new ConnectionInfo(profile, numConnectionsSupported); 142 } 143 144 /** 145 * Set the priority of the device with the given priority level 146 * 147 * @param deviceToTag - BluetoothDevice to set the priority for 148 * @param priority - Priority to set 149 */ 150 151 public void setBluetoothDevicePriorityLocked(BluetoothDevice deviceToTag, int priority) { 152 /*if (priority >= mConnectionInfo.mNumConnectionsSupported) { 153 if (DBG) { 154 Log.d(TAG, "Priority cannot exceed number of connections supported"); 155 } 156 return; 157 }*/ 158 // if there is a device already set to that priority, unseat that device 159 BluetoothDevice oldDeviceWithPriority = getBluetoothDeviceForPriorityLocked(priority); 160 if (oldDeviceWithPriority != null) { 161 if (DBG) { 162 Log.d(TAG, "Unsetting priority " + priority + " on " + oldDeviceWithPriority); 163 } 164 removeBluetoothDevicePriorityLocked(oldDeviceWithPriority); 165 } 166 // Tag the new device with the given priority 167 DeviceInfo newDeviceInfoWithPriority = findDeviceInfoInListLocked(deviceToTag); 168 if (newDeviceInfoWithPriority == null) { 169 if (DBG) { 170 Log.d(TAG, "setBluetoothDevicePriorityLocked():Unknown and unpaired device"); 171 } 172 return; 173 } 174 if (DBG) { 175 Log.d(TAG, "Setting priority " + priority + " to " 176 + newDeviceInfoWithPriority.mBluetoothDevice); 177 } 178 newDeviceInfoWithPriority.setBluetoothDevicePriority(priority); 179 // Update the position of the device in the device Queue 180 moveDeviceToPrioritySlotsLocked(newDeviceInfoWithPriority, priority); 181 } 182 183 /** 184 * Clear the priority of the given device. 185 * 186 * @param deviceToUntag - BluetoothDevice to untag 187 */ 188 public void removeBluetoothDevicePriorityLocked(BluetoothDevice deviceToUntag) { 189 DeviceInfo deviceInfo = findDeviceInfoInListLocked(deviceToUntag); 190 deviceInfo.setBluetoothDevicePriority(DEVICE_PRIORITY_UNDEFINED); 191 } 192 193 /** 194 * Returns the number of devices that have been tagged as priority devices. 195 * If there is a device that is tagged as a Secondary device, then the number of tagged devices 196 * is 2, even if there is no primary device. 197 * 198 * @return - Number of Tagged devices Ex: Only Primary - 1, Primary and/or Secondary - 2 199 */ 200 public int getNumberOfTaggedDevicesLocked() { 201 int numberOfTaggedDevices = 0; 202 if (getBluetoothDeviceForPriorityLocked( 203 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1) != null) { 204 return CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1 + 1; 205 } else if (getBluetoothDeviceForPriorityLocked( 206 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) != null) { 207 return CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0 + 1; 208 } 209 return numberOfTaggedDevices; 210 } 211 212 /** 213 * Returns the device that has the passed priority 214 */ 215 public BluetoothDevice getBluetoothDeviceForPriorityLocked(int priority) { 216 BluetoothDevice device = null; 217 for (DeviceInfo deviceInfo : mDeviceInfoList) { 218 if (deviceInfo.mDevicePriority == priority) { 219 return deviceInfo.mBluetoothDevice; 220 } 221 } 222 return device; 223 } 224 225 /** 226 * Get the position of the given device in the list of connectable devices for this profile. 227 * 228 * @param device - {@link BluetoothDevice} 229 * @return postion in the {@link #mDeviceInfoList}, DEVICE_NOT_FOUND if the device is not in the 230 * list. 231 */ 232 private int getPositionInListLocked(BluetoothDevice device) { 233 int index = DEVICE_NOT_FOUND; 234 if (mDeviceInfoList != null) { 235 int i = 0; 236 for (DeviceInfo devInfo : mDeviceInfoList) { 237 if (devInfo.mBluetoothDevice.getAddress().equals(device.getAddress())) { 238 index = i; 239 break; 240 } 241 i++; 242 } 243 } 244 return index; 245 } 246 247 /** 248 * Check if the given device is in the {@link #mDeviceInfoList} 249 * 250 * @param device - {@link BluetoothDevice} to look for 251 * @return true if found, false if not found 252 */ 253 boolean checkDeviceInListLocked(BluetoothDevice device) { 254 boolean isPresent = false; 255 if (device == null) { 256 return isPresent; 257 } 258 for (DeviceInfo devInfo : mDeviceInfoList) { 259 if (devInfo.mBluetoothDevice.getAddress().equals(device.getAddress())) { 260 isPresent = true; 261 break; 262 } 263 } 264 return isPresent; 265 } 266 267 /** 268 * Iterate through the {@link BluetoothDevicesInfo#mDeviceInfoList} and find the 269 * {@link DeviceInfo} with the given {@link BluetoothDevice} 270 * 271 * @param device - {@link BluetoothDevice} to look for 272 * @return - {@link DeviceInfo} that contains the passed {@link BluetoothDevice} 273 */ 274 private DeviceInfo findDeviceInfoInListLocked(@Nullable BluetoothDevice device) { 275 if (device == null) { 276 return null; 277 } 278 for (DeviceInfo devInfo : mDeviceInfoList) { 279 if (devInfo.mBluetoothDevice.getAddress().equals(device.getAddress())) { 280 return devInfo; 281 } 282 } 283 return null; 284 } 285 286 /** 287 * Get the current list of connectable devices for this profile. 288 * 289 * @return Device list for this profile. 290 */ 291 public List<BluetoothDevice> getDeviceList() { 292 List<BluetoothDevice> bluetoothDeviceList = new ArrayList<>(); 293 for (DeviceInfo deviceInfo : mDeviceInfoList) { 294 bluetoothDeviceList.add(deviceInfo.mBluetoothDevice); 295 } 296 return bluetoothDeviceList; 297 } 298 299 public List<DeviceInfo> getDeviceInfoList() { 300 return mDeviceInfoList; 301 } 302 303 public void setNumberOfConnectionsSupported(int num) { 304 mConnectionInfo.mNumConnectionsSupported = num; 305 } 306 307 public int getNumberOfConnectionsSupported() { 308 return mConnectionInfo.mNumConnectionsSupported; 309 } 310 311 /** 312 * Add a device to the device list. Used during pairing. 313 * 314 * @param dev - device to add for further connection attempts on this profile. 315 */ 316 public void addDeviceLocked(BluetoothDevice dev) { 317 // Check if this device is already in the device list 318 if (checkDeviceInListLocked(dev)) { 319 if (DBG) { 320 Log.d(TAG, "Device " + dev + " already in list. Not adding"); 321 } 322 return; 323 } 324 // Add new device and set the connection state to DISCONNECTED. 325 if (mDeviceInfoList != null) { 326 DeviceInfo deviceInfo = new DeviceInfo(dev, BluetoothProfile.STATE_DISCONNECTED); 327 mDeviceInfoList.add(deviceInfo); 328 } else { 329 if (DBG) { 330 Log.d(TAG, "Device List is null"); 331 } 332 } 333 } 334 335 /** 336 * Set the connection state for this device to the given connection state. 337 * 338 * @param device - Bluetooth device to update the state for 339 * @param state - the Connection state to set. 340 */ 341 public void setConnectionStateLocked(BluetoothDevice device, int state) { 342 if (device == null) { 343 Log.e(TAG, "setConnectionStateLocked() device null"); 344 return; 345 } 346 for (DeviceInfo devInfo : mDeviceInfoList) { 347 BluetoothDevice dev = devInfo.mBluetoothDevice; 348 if (dev == null) { 349 continue; 350 } 351 if (dev.getAddress().equals(device.getAddress())) { 352 if (DBG) { 353 Log.d(TAG, "Setting " + dev + " state to " + state); 354 } 355 devInfo.setConnectionState(state); 356 break; 357 } 358 } 359 } 360 361 /** 362 * Returns the current connection state for the given device 363 * 364 * @param device - device to get the bluetooth connection state for 365 * @return - Connection State. If passed device is null, returns DEVICE_NOT_FOUND. 366 */ 367 public int getCurrentConnectionStateLocked(BluetoothDevice device) { 368 int state = DEVICE_NOT_FOUND; 369 if (device == null) { 370 Log.e(TAG, "getCurrentConnectionStateLocked() device null"); 371 return state; 372 } 373 374 for (DeviceInfo devInfo : mDeviceInfoList) { 375 BluetoothDevice dev = devInfo.mBluetoothDevice; 376 if (dev == null) { 377 continue; 378 } 379 if (dev.getAddress().equals(device.getAddress())) { 380 state = devInfo.getConnectionState(); 381 break; 382 } 383 } 384 return state; 385 } 386 387 /** 388 * Returns the device that is currently in the middle of a connection attempt. 389 * 390 * @return BluetoothDevice that is connecting, null if no device is connecting 391 */ 392 public BluetoothDevice getConnectingDeviceLocked() { 393 for (DeviceInfo devInfo : mDeviceInfoList) { 394 if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTING) { 395 return devInfo.getBluetoothDevice(); 396 } 397 } 398 return null; 399 } 400 401 /** 402 * Returns a list of connected devices for this profile. 403 * 404 * @return - List of connected devices 405 */ 406 public List<BluetoothDevice> getConnectedDevicesLocked() { 407 List<BluetoothDevice> devices = new ArrayList<>(); 408 for (DeviceInfo devInfo : mDeviceInfoList) { 409 if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTED) { 410 devices.add(devInfo.getBluetoothDevice()); 411 } 412 } 413 if (DBG) { 414 Log.d(TAG, "Active Connections: " + getNumberOfActiveConnectionsLocked()); 415 Log.d(TAG, "Connected devices Size: " + devices.size()); 416 } 417 return devices; 418 } 419 420 /** 421 * Remove a device from the list. Used when a device is unpaired 422 * 423 * @param dev - device to remove from the list. 424 */ 425 public void removeDeviceLocked(BluetoothDevice dev) { 426 if (mDeviceInfoList != null) { 427 DeviceInfo devInfo = findDeviceInfoInListLocked(dev); 428 if (devInfo != null) { 429 mDeviceInfoList.remove(devInfo); 430 // If the device was connected when it was unpaired, we wouldn't have received the 431 // Profile disconnected intents. Hence check if the device was connected and if it 432 // was, then decrement the number of active connections. 433 if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTED) { 434 mConnectionInfo.mNumActiveConnections--; 435 } 436 } 437 } else { 438 if (DBG) { 439 Log.d(TAG, "Device List is null"); 440 } 441 } 442 Log.d(TAG, "Device List size: " + mDeviceInfoList.size()); 443 } 444 445 public void clearDeviceListLocked() { 446 if (mDeviceInfoList != null) { 447 mDeviceInfoList.clear(); 448 } 449 } 450 451 /** 452 * Returns the next device to attempt a connection on for this profile. 453 * 454 * @return {@link BluetoothDevice} that is next in the Queue. null if the Queue has been 455 * exhausted 456 * (no known device nearby) 457 */ 458 public BluetoothDevice getNextDeviceInQueueLocked() { 459 BluetoothDevice device = null; 460 int numberOfPairedDevices = getNumberOfPairedDevicesLocked(); 461 // iterate till we find a disconnected device 462 while (mConnectionInfo.mDeviceIndex < numberOfPairedDevices && 463 mDeviceInfoList.get(mConnectionInfo.mDeviceIndex).getConnectionState() 464 != BluetoothProfile.STATE_DISCONNECTED) { 465 mConnectionInfo.mDeviceIndex++; 466 } 467 468 if (mConnectionInfo.mDeviceIndex >= numberOfPairedDevices) { 469 if (DBG) { 470 Log.d(TAG, 471 "No device available for profile " 472 + mConnectionInfo.mProfile + " " 473 + mConnectionInfo.mDeviceIndex + "/" 474 + numberOfPairedDevices); 475 } 476 // mDeviceIndex is the index of the device in the mDeviceInfoList, that the next 477 // connection attempt would be made on. It is moved ahead on 478 // updateConnectionStatusLocked() so it always holds the index of the next device to 479 // connect to. But here, when we get the next device to connect to, if we see that 480 // the index is greater than the number of devices in the list, then we move the index 481 // back to the first device in the list and don't return anything. 482 // The reason why this is reset is to imply that connection attempts on this profile has 483 // been exhausted and if you want to retry connecting on this profile, we will start 484 // from the first device. 485 // The reason to reset here rather than in updateConnectionStatusLocked() is to make 486 // sure we have the latest view of the numberOfPairedDevices before we say we have 487 // exhausted the list. 488 mConnectionInfo.mDeviceIndex = 0; 489 return null; 490 } 491 492 device = mDeviceInfoList.get(mConnectionInfo.mDeviceIndex).mBluetoothDevice; 493 if (DBG) { 494 Log.d(TAG, "Getting device " + mConnectionInfo.mDeviceIndex + " from list: " 495 + device); 496 } 497 return device; 498 } 499 500 /** 501 * Update the connection Status for connection attempts made on this profile. 502 * If the attempt was successful, mark it and keep track of the device that was connected. 503 * If unsuccessful, check if we can retry on the same device. If no more retry attempts, 504 * move to the next device in the Queue. 505 * 506 * @param device - {@link BluetoothDevice} that connected. 507 * @param success - connection result 508 * @param retry - If Retries are available for the same device. 509 */ 510 public void updateConnectionStatusLocked(BluetoothDevice device, boolean success, 511 boolean retry) { 512 if (device == null) { 513 Log.w(TAG, "Updating Status with null BluetoothDevice"); 514 return; 515 } 516 if (success) { 517 if (DBG) { 518 Log.d(TAG, mConnectionInfo.mProfile + " connected to " + device); 519 } 520 // Get the position of this device in the device list maintained for this profile. 521 int positionInQ = getPositionInListLocked(device); 522 if (DBG) { 523 Log.d(TAG, "Position of " + device + " in Q: " + positionInQ); 524 } 525 // If the device that connected is not in the list, it could be because it is being 526 // paired and getting added to the device list for this profile for the first time. 527 if (positionInQ == DEVICE_NOT_FOUND) { 528 Log.d(TAG, "Connected device not in Q: " + device); 529 addDeviceLocked(device); 530 positionInQ = mDeviceInfoList.size() - 1; 531 } else if (positionInQ != mConnectionInfo.mDeviceIndex) { 532 /* 533 This will happen if auto-connect requests to connect on a device from its list, 534 but the device that connected was different. Maybe there was another requestor 535 and the Bluetooth services chose to honor the other request. What we do here, 536 is to make sure we note which device connected and not assume that the device 537 that connected is the device we requested. The ultimate goal of the policy is 538 to remember which devices connected on which profile (regardless of the origin 539 of the connection request) so it knows which device to connect the next time. 540 */ 541 if (DBG) { 542 Log.d(TAG, "Different device connected: " + device + " CurrIndex: " 543 + mConnectionInfo.mDeviceIndex); 544 } 545 } 546 547 // At this point positionInQ reflects where in the list the device that connected is, 548 // i.e, its index. Move the device to the front of the device list, since the policy is 549 // to try to connect to the last connected device first. Hence by moving the device 550 // to the front of the list, the next time auto connect triggers, this will be the 551 // device that the policy will try to connect on for this profile. 552 if (positionInQ != 0) { 553 moveDeviceToQueueFrontLocked(positionInQ); 554 // reset the device Index back to the first in the Queue 555 //mConnectionInfo.mDeviceIndex = 0; 556 } 557 558 if (getCurrentConnectionStateLocked(device) != BluetoothProfile.STATE_CONNECTED) { 559 mConnectionInfo.mNumActiveConnections++; 560 if (DBG) { 561 Log.d(TAG, 562 "Incrementing Active Connections " 563 + mConnectionInfo.mNumActiveConnections); 564 } 565 } 566 setConnectionStateLocked(device, BluetoothProfile.STATE_CONNECTED); 567 // Reset the retry count 568 mConnectionInfo.mRetryAttempt = 0; 569 570 if (getConnectingDeviceLocked() == null) { 571 mConnectionInfo.mDeviceIndex++; 572 } 573 if (DBG) { 574 Log.d(TAG, 575 "Profile: " + mConnectionInfo.mProfile + " Number of Active Connections: " 576 + mConnectionInfo.mNumActiveConnections); 577 } 578 } else { 579 // if no more retries, move to the next device 580 if (DBG) { 581 Log.d(TAG, "Connection fail or Disconnected"); 582 } 583 // Decrement Number of Active Connections only if the device is already connected. 584 if (getCurrentConnectionStateLocked(device) == BluetoothProfile.STATE_CONNECTED) { 585 mConnectionInfo.mNumActiveConnections--; 586 if (DBG) { 587 Log.d(TAG, "Decrementing Active Connections " 588 + mConnectionInfo.mNumActiveConnections); 589 } 590 } 591 setConnectionStateLocked(device, BluetoothProfile.STATE_DISCONNECTED); 592 593 // Update the mDeviceIndex only when there is no other device currently in the middle 594 // of a connection attempt. This is to safeguard against disconnect intents coming in 595 // for devices other than the one that the policy is currently trying to connect. 596 if (getConnectingDeviceLocked() == null) { 597 if (!retry) { 598 mConnectionInfo.mDeviceIndex++; 599 if (DBG) { 600 Log.d(TAG, "Moving to device: " + mConnectionInfo.mDeviceIndex); 601 } 602 // Reset the retry count 603 mConnectionInfo.mRetryAttempt = 0; 604 } else { 605 if (DBG) { 606 Log.d(TAG, "Staying with the same device - retrying: " 607 + mConnectionInfo.mDeviceIndex); 608 } 609 } 610 } else { 611 BluetoothDevice connectingDevice = getConnectingDeviceLocked(); 612 if (connectingDevice != null) { 613 if (DBG) { 614 Log.d(TAG, "Not moving to next device. " + connectingDevice 615 + " still connecting"); 616 } 617 } else { 618 Log.e(TAG, "Unexpected. Status = connecting, but connecting device = null"); 619 } 620 } 621 } 622 } 623 624 /** 625 * Move the given device to its priority slot 626 * 627 * @param deviceInfo - DeviceInfo to move 628 * @param priority - Priority of the device in the list 629 */ 630 private void moveDeviceToPrioritySlotsLocked(DeviceInfo deviceInfo, int priority) { 631 if (DBG) { 632 Log.d(TAG, "Moving " + deviceInfo.mBluetoothDevice + " to " + priority); 633 } 634 mDeviceInfoList.remove(deviceInfo); 635 mDeviceInfoList.add(priority, deviceInfo); 636 } 637 638 /** 639 * Move the item in the given position to the front of the queue and push the rest down. 640 * 641 * @param position - current position of the device that it is moving from 642 */ 643 private void moveDeviceToQueueFrontLocked(int position) { 644 int topOfList = getNumberOfTaggedDevicesLocked(); 645 // If the device is a primary or secondary, its position is fixed. 646 if (position <= topOfList) { 647 return; 648 } 649 DeviceInfo deviceInfo = mDeviceInfoList.get(position); 650 if (deviceInfo.mBluetoothDevice == null) { 651 if (DBG) { 652 Log.d(TAG, "Unexpected: deviceToMove is null"); 653 } 654 return; 655 } 656 mDeviceInfoList.remove(position); 657 // Top of the list to which a device can be moved depends on the number of tagged devices 658 // If there is a dedicated Primary device, then the newly connected device can only be moved 659 // to the second position, since the primary device always occupies the first position. 660 // Hence the topOfList is the first position after the tagged devices. 661 mDeviceInfoList.add(topOfList, deviceInfo); 662 } 663 664 /** 665 * Returns the profile that this devicesInfo is for. 666 */ 667 public Integer getProfileLocked() { 668 return mConnectionInfo.mProfile; 669 } 670 671 /** 672 * Get the number of devices in the {@link #mDeviceInfoList} - paired and previously connected 673 * devices 674 * 675 * @return number of paired devices on this profile. 676 */ 677 public int getNumberOfPairedDevicesLocked() { 678 return mDeviceInfoList.size(); 679 } 680 681 /** 682 * Increment the retry count. Called when a connection is made on the profile. 683 */ 684 public void incrementRetryCountLocked() { 685 if (mConnectionInfo != null) { 686 mConnectionInfo.mRetryAttempt++; 687 } 688 } 689 690 /** 691 * Get the number of times a connection attempt has been tried on a device for this profile. 692 * 693 * @return number of retry attempts. 694 */ 695 public Integer getRetryCountLocked() { 696 return mConnectionInfo.mRetryAttempt; 697 } 698 699 /** 700 * Set the mDeviceAvailableToConnect with the passed value. 701 * 702 * @param deviceAvailable - true or false. 703 */ 704 public void setDeviceAvailableToConnectLocked(boolean deviceAvailable) { 705 mConnectionInfo.mDeviceAvailableToConnect = deviceAvailable; 706 } 707 708 /** 709 * Returns if there are any devices available to connect on this profile. 710 * 711 * @return true if a device is available, false 712 * 1. if number of active connections on this profile has been maxed out or 713 * 2. if all devices in the list have failed to connect already. 714 */ 715 public boolean isProfileConnectableLocked() { 716 if (DBG) { 717 Log.d(TAG, "Profile: " + mConnectionInfo.mProfile + " Num of connections: " 718 + mConnectionInfo.mNumActiveConnections + " Conn Supported: " 719 + mConnectionInfo.mNumConnectionsSupported 720 + ", the flag: mDeviceAvailableToConnect = " 721 + mConnectionInfo.mDeviceAvailableToConnect); 722 } 723 724 if (mConnectionInfo.mDeviceAvailableToConnect && 725 mConnectionInfo.mNumActiveConnections < mConnectionInfo.mNumConnectionsSupported) { 726 return true; 727 } 728 729 if (DBG) { 730 if (mConnectionInfo.mDeviceAvailableToConnect) { 731 Log.d(TAG, "Connected devices for profile " + mConnectionInfo.mProfile); 732 for (BluetoothDevice device : getConnectedDevicesLocked()) { 733 Log.d(TAG, device.getAddress()); 734 } 735 } 736 } 737 return false; 738 } 739 740 /** 741 * Return the current number of active connections on this profile. 742 * 743 * @return number of active connections. 744 */ 745 public int getNumberOfActiveConnectionsLocked() { 746 return mConnectionInfo.mNumActiveConnections; 747 } 748 749 public void resetDeviceIndex() { 750 mConnectionInfo.mDeviceIndex = 0; 751 } 752 753 /** 754 * Reset the connection related bookkeeping information. 755 * Called on a BluetoothAdapter Off to clean slate 756 */ 757 public void resetConnectionInfoLocked() { 758 mConnectionInfo.mNumActiveConnections = 0; 759 mConnectionInfo.mDeviceIndex = 0; 760 mConnectionInfo.mRetryAttempt = 0; 761 mConnectionInfo.mDeviceAvailableToConnect = true; 762 for (DeviceInfo info : mDeviceInfoList) { 763 setConnectionStateLocked(info.getBluetoothDevice(), 764 BluetoothProfile.STATE_DISCONNECTED); 765 } 766 } 767 768 public void resetDeviceListLocked() { 769 if (mDeviceInfoList != null) { 770 mDeviceInfoList.clear(); 771 } 772 resetConnectionInfoLocked(); 773 } 774 775 @VisibleForTesting 776 String toDebugString() { 777 StringBuilder buf = new StringBuilder(); 778 buf.append("\tmDeviceAvailableToConnect = " + mConnectionInfo.mDeviceAvailableToConnect); 779 buf.append("\n\t#Paired Devices = " + getNumberOfPairedDevicesLocked()); 780 buf.append(", #Active Connections = " + mConnectionInfo.mNumActiveConnections); 781 buf.append(", #Connections Supported = " + mConnectionInfo.mNumConnectionsSupported); 782 if (mDeviceInfoList != null) { 783 int i = 1; 784 for (DeviceInfo devInfo : mDeviceInfoList) { 785 buf.append("\n\t\tdevice# " + i++ + 786 " = " + Utils.getDeviceDebugInfo(devInfo.getBluetoothDevice())); 787 String s; 788 switch (devInfo.getConnectionState()) { 789 case BluetoothProfile.STATE_CONNECTING: 790 s = "STATE_CONNECTING"; 791 break; 792 case BluetoothProfile.STATE_DISCONNECTED: 793 s = "STATE_DISCONNECTED"; 794 break; 795 case BluetoothProfile.STATE_CONNECTED: 796 s = "STATE_CONNECTED"; 797 break; 798 default: 799 s = "state_UNKNOWN"; 800 } 801 buf.append(", " + s); 802 } 803 } else { 804 buf.append("\n\tno devices listed"); 805 } 806 buf.append("\n"); 807 return buf.toString(); 808 } 809} 810