• 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 import 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  */
48 public 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 
ConnectionInfo(int profile)78         public ConnectionInfo(int profile) {
79             // Default the number of concurrent active connections supported to 1.
80             this(profile, 1);
81         }
82 
ConnectionInfo(int profile, int numConnectionsSupported)83         public ConnectionInfo(int profile, int numConnectionsSupported) {
84             mProfile = profile;
85             mNumConnectionsSupported = numConnectionsSupported;
86             initConnectionInfo();
87         }
88 
initConnectionInfo()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 
DeviceInfo(BluetoothDevice device, int state)107         public DeviceInfo(BluetoothDevice device, int state) {
108             mBluetoothDevice = device;
109             mConnectionState = state;
110             mDevicePriority = DEVICE_PRIORITY_UNDEFINED;
111         }
112 
setConnectionState(int state)113         public void setConnectionState(int state) {
114             mConnectionState = state;
115         }
116 
getConnectionState()117         public int getConnectionState() {
118             return mConnectionState;
119         }
120 
getBluetoothDevice()121         public BluetoothDevice getBluetoothDevice() {
122             return mBluetoothDevice;
123         }
124 
setBluetoothDevicePriority(int priority)125         public void setBluetoothDevicePriority(int priority) {
126             mDevicePriority = priority;
127         }
128 
getBluetoothDevicePriority()129         public int getBluetoothDevicePriority() {
130             return mDevicePriority;
131         }
132     }
133 
BluetoothDevicesInfo(int profile)134     public BluetoothDevicesInfo(int profile) {
135         mDeviceInfoList = new ArrayList<>();
136         mConnectionInfo = new ConnectionInfo(profile);
137     }
138 
BluetoothDevicesInfo(int profile, int numConnectionsSupported)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 
setBluetoothDevicePriorityLocked(BluetoothDevice deviceToTag, int priority)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      */
removeBluetoothDevicePriorityLocked(BluetoothDevice deviceToUntag)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      */
getNumberOfTaggedDevicesLocked()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      */
getBluetoothDeviceForPriorityLocked(int priority)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      */
getPositionInListLocked(BluetoothDevice device)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      */
checkDeviceInListLocked(BluetoothDevice device)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      */
findDeviceInfoInListLocked(@ullable BluetoothDevice device)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      */
getDeviceList()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 
getDeviceInfoList()299     public List<DeviceInfo> getDeviceInfoList() {
300         return mDeviceInfoList;
301     }
302 
setNumberOfConnectionsSupported(int num)303     public void setNumberOfConnectionsSupported(int num) {
304         mConnectionInfo.mNumConnectionsSupported = num;
305     }
306 
getNumberOfConnectionsSupported()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      */
addDeviceLocked(BluetoothDevice dev)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      */
setConnectionStateLocked(BluetoothDevice device, int state)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      */
getCurrentConnectionStateLocked(BluetoothDevice device)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      */
getConnectingDeviceLocked()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      */
getConnectedDevicesLocked()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      */
removeDeviceLocked(BluetoothDevice dev)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 
clearDeviceListLocked()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      */
getNextDeviceInQueueLocked()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      */
updateConnectionStatusLocked(BluetoothDevice device, boolean success, boolean retry)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      */
moveDeviceToPrioritySlotsLocked(DeviceInfo deviceInfo, int priority)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      */
moveDeviceToQueueFrontLocked(int position)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      */
getProfileLocked()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      */
getNumberOfPairedDevicesLocked()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      */
incrementRetryCountLocked()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      */
getRetryCountLocked()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      */
setDeviceAvailableToConnectLocked(boolean deviceAvailable)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      */
isProfileConnectableLocked()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      */
getNumberOfActiveConnectionsLocked()745     public int getNumberOfActiveConnectionsLocked() {
746         return mConnectionInfo.mNumActiveConnections;
747     }
748 
resetDeviceIndex()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      */
resetConnectionInfoLocked()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 
resetDeviceListLocked()768     public void resetDeviceListLocked() {
769         if (mDeviceInfoList != null) {
770             mDeviceInfoList.clear();
771         }
772         resetConnectionInfoLocked();
773     }
774 
775     @VisibleForTesting
toDebugString()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