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