• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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