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.bluetooth.btservice;
18 
19 import static com.android.bluetooth.Utils.isDualModeAudioEnabled;
20 
21 import android.annotation.RequiresPermission;
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothCsipSetCoordinator;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothProfile;
26 import android.bluetooth.BluetoothProtoEnums;
27 import android.bluetooth.BluetoothUuid;
28 import android.content.IntentFilter;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.ParcelUuid;
33 import android.os.SystemProperties;
34 import android.util.Log;
35 
36 import com.android.bluetooth.R;
37 import com.android.bluetooth.Utils;
38 import com.android.bluetooth.a2dp.A2dpService;
39 import com.android.bluetooth.bas.BatteryService;
40 import com.android.bluetooth.bass_client.BassClientService;
41 import com.android.bluetooth.btservice.storage.DatabaseManager;
42 import com.android.bluetooth.csip.CsipSetCoordinatorService;
43 import com.android.bluetooth.flags.Flags;
44 import com.android.bluetooth.hap.HapClientService;
45 import com.android.bluetooth.hearingaid.HearingAidService;
46 import com.android.bluetooth.hfp.HeadsetService;
47 import com.android.bluetooth.hid.HidHostService;
48 import com.android.bluetooth.le_audio.LeAudioService;
49 import com.android.bluetooth.pan.PanService;
50 import com.android.bluetooth.vc.VolumeControlService;
51 import com.android.internal.annotations.VisibleForTesting;
52 
53 import java.util.ArrayList;
54 import java.util.HashSet;
55 import java.util.List;
56 import java.util.Objects;
57 
58 // Describes the phone policy
59 //
60 // Policies are usually governed by outside events that may warrant an action. We talk about various
61 // events and the resulting outcome from this policy:
62 //
63 // 1. Adapter turned ON: At this point we will try to auto-connect the (device, profile) pairs which
64 // have PRIORITY_AUTO_CONNECT. The fact that we *only* auto-connect Headset and A2DP is something
65 // that is hardcoded and specific to phone policy (see autoConnect() function)
66 // 2. When the profile connection-state changes: At this point if a new profile gets CONNECTED we
67 // will try to connect other profiles on the same device. This is to avoid collision if devices
68 // somehow end up trying to connect at same time or general connection issues.
69 public class PhonePolicy implements AdapterService.BluetoothStateCallback {
70     private static final String TAG = "BluetoothPhonePolicy";
71 
72     // Message types for the handler (internal messages generated by intents or timeouts)
73     private static final int MESSAGE_CONNECT_OTHER_PROFILES = 3;
74 
75     @VisibleForTesting
76     static final String AUTO_CONNECT_PROFILES_PROPERTY = "bluetooth.auto_connect_profiles.enabled";
77 
78     private static final String LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY =
79             "ro.bluetooth.leaudio.le_audio_connection_by_default";
80 
81     @VisibleForTesting
82     static final String BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY =
83             "persist.bluetooth.leaudio.bypass_allow_list";
84 
85     // Timeouts
86     @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s
87 
88     private DatabaseManager mDatabaseManager;
89     private final AdapterService mAdapterService;
90     private final ServiceFactory mFactory;
91     private final Handler mHandler;
92     private final HashSet<BluetoothDevice> mHeadsetRetrySet = new HashSet<>();
93     private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>();
94     private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>();
95     @VisibleForTesting boolean mAutoConnectProfilesSupported;
96     @VisibleForTesting boolean mLeAudioEnabledByDefault;
97 
98     @Override
onBluetoothStateChange(int prevState, int newState)99     public void onBluetoothStateChange(int prevState, int newState) {
100         // Only act if the adapter has actually changed state from non-ON to ON.
101         // NOTE: ON is the state depicting BREDR ON and not just BLE ON.
102         if (newState == BluetoothAdapter.STATE_ON) {
103             resetStates();
104             autoConnect();
105         }
106     }
107 
profileConnectionStateChanged( int profile, BluetoothDevice device, int fromState, int toState)108     public void profileConnectionStateChanged(
109             int profile, BluetoothDevice device, int fromState, int toState) {
110         switch (profile) {
111             case BluetoothProfile.A2DP:
112             case BluetoothProfile.HEADSET:
113             case BluetoothProfile.LE_AUDIO:
114             case BluetoothProfile.CSIP_SET_COORDINATOR:
115             case BluetoothProfile.VOLUME_CONTROL:
116                 mHandler.post(
117                         () -> processProfileStateChanged(device, profile, toState, fromState));
118                 break;
119             default:
120                 break;
121         }
122     }
123 
124     /**
125      * Called when active state of audio profiles changed
126      *
127      * @param profile The Bluetooth profile of which active state changed
128      * @param device The device currently activated. {@code null} if no A2DP device activated
129      */
profileActiveDeviceChanged(int profile, BluetoothDevice device)130     public void profileActiveDeviceChanged(int profile, BluetoothDevice device) {
131         mHandler.post(() -> processActiveDeviceChanged(device, profile));
132     }
133 
handleAclConnected(BluetoothDevice device)134     public void handleAclConnected(BluetoothDevice device) {
135         mHandler.post(() -> processDeviceConnected(device));
136     }
137 
138     // Handler to handoff intents to class thread
139     class PhonePolicyHandler extends Handler {
PhonePolicyHandler(Looper looper)140         PhonePolicyHandler(Looper looper) {
141             super(looper);
142         }
143 
144         @Override
handleMessage(Message msg)145         public void handleMessage(Message msg) {
146             switch (msg.what) {
147                 case MESSAGE_CONNECT_OTHER_PROFILES:
148                     {
149                         // Called when we try connect some profiles in processConnectOtherProfiles
150                         // but
151                         // we send a delayed message to try connecting the remaining profiles
152                         BluetoothDevice device = (BluetoothDevice) msg.obj;
153                         processConnectOtherProfiles(device);
154                         mConnectOtherProfilesDeviceSet.remove(device);
155                         break;
156                     }
157             }
158         }
159     }
160     ;
161 
162     // Policy API functions for lifecycle management (protected)
start()163     public void start() {
164         mAdapterService.registerBluetoothStateCallback((command) -> mHandler.post(command), this);
165 
166         IntentFilter filter = new IntentFilter();
167         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
168         filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
169     }
170 
cleanup()171     public void cleanup() {
172         mAdapterService.unregisterBluetoothStateCallback(this);
173         resetStates();
174     }
175 
PhonePolicy(AdapterService service, ServiceFactory factory)176     PhonePolicy(AdapterService service, ServiceFactory factory) {
177         mAdapterService = service;
178         mDatabaseManager = Objects.requireNonNull(service.getDatabase());
179         mFactory = factory;
180         mHandler = new PhonePolicyHandler(service.getMainLooper());
181         mAutoConnectProfilesSupported =
182                 SystemProperties.getBoolean(AUTO_CONNECT_PROFILES_PROPERTY, false);
183         mLeAudioEnabledByDefault =
184                 SystemProperties.getBoolean(LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY, true);
185     }
186 
isLeAudioOnlyGroup(BluetoothDevice device)187     boolean isLeAudioOnlyGroup(BluetoothDevice device) {
188         if (!Flags.leaudioAllowLeaudioOnlyDevices()) {
189             debugLog(" leaudio_allow_leaudio_only_devices is not enabled ");
190             return false;
191         }
192 
193         CsipSetCoordinatorService csipSetCoordinatorService =
194                 mFactory.getCsipSetCoordinatorService();
195 
196         if (csipSetCoordinatorService == null) {
197             debugLog("isLeAudioOnlyGroup: no csip service known yet for " + device);
198             return false;
199         }
200 
201         int groupId = csipSetCoordinatorService.getGroupId(device, BluetoothUuid.CAP);
202         if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
203             debugLog("isLeAudioOnlyGroup: no LeAudio groupID yet known for " + device);
204             return false;
205         }
206 
207         int groupSize = csipSetCoordinatorService.getDesiredGroupSize(groupId);
208         List<BluetoothDevice> groupDevices =
209                 csipSetCoordinatorService.getGroupDevicesOrdered(groupId);
210 
211         if (groupDevices.size() != groupSize) {
212             debugLog(
213                     "isLeAudioOnlyGroup: Group is not yet complete ("
214                             + groupDevices.size()
215                             + " != "
216                             + groupSize
217                             + ")");
218             return false;
219         }
220 
221         for (BluetoothDevice dev : groupDevices) {
222             int remoteType = mAdapterService.getRemoteType(dev);
223             debugLog("isLeAudioOnlyGroup: " + dev + " is type: " + remoteType);
224 
225             if (remoteType != BluetoothDevice.DEVICE_TYPE_LE) {
226                 debugLog("isLeAudioOnlyGroup: " + dev + " is type: " + remoteType);
227                 return false;
228             }
229 
230             if (!mAdapterService.isProfileSupported(dev, BluetoothProfile.LE_AUDIO)) {
231                 debugLog("isLeAudioOnlyGroup: " + dev + " does not support LE AUDIO");
232                 return false;
233             }
234 
235             if (mAdapterService.isProfileSupported(dev, BluetoothProfile.HEARING_AID)) {
236                 debugLog("isLeAudioOnlyGroup: " + dev + " supports ASHA");
237                 return false;
238             }
239         }
240 
241         return true;
242     }
243 
isLeAudioOnlyDevice(BluetoothDevice device, ParcelUuid[] uuids)244     boolean isLeAudioOnlyDevice(BluetoothDevice device, ParcelUuid[] uuids) {
245         /* This functions checks if device belongs to the LeAudio group which
246          * is LeAudio only. This is either
247          * - LeAudio only Headset (no BR/EDR mode)
248          * - LeAudio Hearing Aid  (no ASHA)
249          *
250          * Note, that we need to have all set bonded to take the decision.
251          * If the set is not bonded, we cannot assume that.
252          */
253 
254         if (!Flags.leaudioAllowLeaudioOnlyDevices()) {
255             debugLog(" leaudio_allow_leaudio_only_devices is not enabled ");
256             return false;
257         }
258 
259         if (!Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO)) {
260             return false;
261         }
262 
263         int deviceType = mAdapterService.getRemoteType(device);
264 
265         if (deviceType != BluetoothDevice.DEVICE_TYPE_LE) {
266             debugLog("isLeAudioOnlyDevice: " + device + " is type" + deviceType);
267             return false;
268         }
269 
270         if (Utils.arrayContains(uuids, BluetoothUuid.HEARING_AID)) {
271             debugLog("isLeAudioOnlyDevice: " + device + " supports ASHA");
272             return false;
273         }
274 
275         /* For no CSIS device, allow LE Only devices. */
276         if (!Utils.arrayContains(uuids, BluetoothUuid.COORDINATED_SET)) {
277             debugLog("isLeAudioOnlyDevice: " + device + " is LeAudio only.");
278             return true;
279         }
280 
281         // For CSIS devices it is bit harder to check.
282         return isLeAudioOnlyGroup(device);
283     }
284 
285     // return true if device support Hearing Access Service and it has not been manually disabled
shouldEnableHapByDefault(BluetoothDevice device, ParcelUuid[] uuids)286     private boolean shouldEnableHapByDefault(BluetoothDevice device, ParcelUuid[] uuids) {
287         if (!Flags.enableHapByDefault()) {
288             Log.i(TAG, "shouldDefaultToHap: Flag enableHapByDefault is disabled");
289             return false;
290         }
291 
292         HapClientService hap = mFactory.getHapClientService();
293         if (hap == null) {
294             Log.e(TAG, "shouldDefaultToHap: HapClient is null");
295             return false;
296         }
297 
298         return Utils.arrayContains(uuids, BluetoothUuid.HAS)
299                 && hap.getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
300     }
301 
302     // Policy implementation, all functions MUST be private
303     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
processInitProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids)304     private void processInitProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids) {
305         debugLog("processInitProfilePriorities() - device " + device);
306         HidHostService hidService = mFactory.getHidHostService();
307         A2dpService a2dpService = mFactory.getA2dpService();
308         HeadsetService headsetService = mFactory.getHeadsetService();
309         PanService panService = mFactory.getPanService();
310         HearingAidService hearingAidService = mFactory.getHearingAidService();
311         LeAudioService leAudioService = mFactory.getLeAudioService();
312         CsipSetCoordinatorService csipSetCoordinatorService =
313                 mFactory.getCsipSetCoordinatorService();
314         VolumeControlService volumeControlService = mFactory.getVolumeControlService();
315         HapClientService hapClientService = mFactory.getHapClientService();
316         BassClientService bcService = mFactory.getBassClientService();
317         BatteryService batteryService = mFactory.getBatteryService();
318 
319         final boolean isBypassLeAudioAllowlist =
320                 SystemProperties.getBoolean(BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY, false);
321 
322         boolean isLeAudioOnly = isLeAudioOnlyDevice(device, uuids);
323         boolean shouldEnableHapByDefault = shouldEnableHapByDefault(device, uuids);
324         boolean isLeAudioProfileAllowed =
325                 (leAudioService != null)
326                         && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO)
327                         && (leAudioService.getConnectionPolicy(device)
328                                 != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)
329                         && (mLeAudioEnabledByDefault || isDualModeAudioEnabled())
330                         && (isBypassLeAudioAllowlist
331                                 || shouldEnableHapByDefault
332                                 || mAdapterService.isLeAudioAllowed(device)
333                                 || isLeAudioOnly);
334         debugLog(
335                 "mLeAudioEnabledByDefault: "
336                         + mLeAudioEnabledByDefault
337                         + ", isBypassLeAudioAllowlist: "
338                         + isBypassLeAudioAllowlist
339                         + ", isLeAudioAllowDevice: "
340                         + mAdapterService.isLeAudioAllowed(device)
341                         + ", mAutoConnectProfilesSupported: "
342                         + mAutoConnectProfilesSupported
343                         + ", isLeAudioProfileAllowed: "
344                         + isLeAudioProfileAllowed
345                         + ", isLeAudioOnly: "
346                         + isLeAudioOnly
347                         + ", shouldEnableHapByDefault: "
348                         + shouldEnableHapByDefault);
349 
350         // Set profile priorities only for the profiles discovered on the remote device.
351         // This avoids needless auto-connect attempts to profiles non-existent on the remote device
352         if ((hidService != null)
353                 && (Utils.arrayContains(uuids, BluetoothUuid.HID)
354                         || Utils.arrayContains(uuids, BluetoothUuid.HOGP)
355                         || (Flags.androidHeadtrackerService()
356                                 && Utils.arrayContains(
357                                         uuids, HidHostService.ANDROID_HEADTRACKER_UUID)))
358                 && (hidService.getConnectionPolicy(device)
359                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
360             if (mAutoConnectProfilesSupported) {
361                 hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
362             } else {
363                 mAdapterService
364                         .getDatabase()
365                         .setProfileConnectionPolicy(
366                                 device,
367                                 BluetoothProfile.HID_HOST,
368                                 BluetoothProfile.CONNECTION_POLICY_ALLOWED);
369             }
370             MetricsLogger.getInstance()
371                     .count(
372                             (Utils.arrayContains(uuids, BluetoothUuid.HID)
373                                             && Utils.arrayContains(uuids, BluetoothUuid.HOGP))
374                                     ? BluetoothProtoEnums.HIDH_COUNT_SUPPORT_BOTH_HID_AND_HOGP
375                                     : BluetoothProtoEnums.HIDH_COUNT_SUPPORT_ONLY_HID_OR_HOGP,
376                             1);
377         }
378 
379         if ((headsetService != null)
380                 && ((Utils.arrayContains(uuids, BluetoothUuid.HSP)
381                                 || Utils.arrayContains(uuids, BluetoothUuid.HFP))
382                         && (headsetService.getConnectionPolicy(device)
383                                 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN))) {
384             if (!isDualModeAudioEnabled() && isLeAudioProfileAllowed) {
385                 debugLog("clear hfp profile priority for the le audio dual mode device " + device);
386                 mAdapterService
387                         .getDatabase()
388                         .setProfileConnectionPolicy(
389                                 device,
390                                 BluetoothProfile.HEADSET,
391                                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
392             } else {
393                 if (mAutoConnectProfilesSupported) {
394                     headsetService.setConnectionPolicy(
395                             device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
396                 } else {
397                     mAdapterService
398                             .getDatabase()
399                             .setProfileConnectionPolicy(
400                                     device,
401                                     BluetoothProfile.HEADSET,
402                                     BluetoothProfile.CONNECTION_POLICY_ALLOWED);
403                 }
404             }
405         }
406 
407         if ((a2dpService != null)
408                 && (Utils.arrayContains(uuids, BluetoothUuid.A2DP_SINK)
409                         || Utils.arrayContains(uuids, BluetoothUuid.ADV_AUDIO_DIST))
410                 && (a2dpService.getConnectionPolicy(device)
411                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
412             if (!isDualModeAudioEnabled() && isLeAudioProfileAllowed) {
413                 debugLog("clear a2dp profile priority for the le audio dual mode device " + device);
414                 mAdapterService
415                         .getDatabase()
416                         .setProfileConnectionPolicy(
417                                 device,
418                                 BluetoothProfile.A2DP,
419                                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
420             } else {
421                 if (mAutoConnectProfilesSupported) {
422                     a2dpService.setConnectionPolicy(
423                             device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
424                 } else {
425                     mAdapterService
426                             .getDatabase()
427                             .setProfileConnectionPolicy(
428                                     device,
429                                     BluetoothProfile.A2DP,
430                                     BluetoothProfile.CONNECTION_POLICY_ALLOWED);
431                 }
432             }
433         }
434 
435         // CSIP should be connected prior to LE Audio
436         if ((csipSetCoordinatorService != null)
437                 && (Utils.arrayContains(uuids, BluetoothUuid.COORDINATED_SET))
438                 && (csipSetCoordinatorService.getConnectionPolicy(device)
439                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
440             // Always allow CSIP during pairing process regardless of LE audio preference
441             if (mAutoConnectProfilesSupported) {
442                 csipSetCoordinatorService.setConnectionPolicy(
443                         device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
444             } else {
445                 mAdapterService
446                         .getDatabase()
447                         .setProfileConnectionPolicy(
448                                 device,
449                                 BluetoothProfile.CSIP_SET_COORDINATOR,
450                                 BluetoothProfile.CONNECTION_POLICY_ALLOWED);
451             }
452         }
453 
454         /* Make sure to connect Volume Control before LeAudio service */
455         if ((volumeControlService != null)
456                 && Utils.arrayContains(uuids, BluetoothUuid.VOLUME_CONTROL)
457                 && (volumeControlService.getConnectionPolicy(device)
458                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
459             if (isLeAudioProfileAllowed) {
460                 debugLog("setting volume control profile priority for device " + device);
461                 if (mAutoConnectProfilesSupported) {
462                     volumeControlService.setConnectionPolicy(
463                             device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
464                 } else {
465                     mAdapterService
466                             .getDatabase()
467                             .setProfileConnectionPolicy(
468                                     device,
469                                     BluetoothProfile.VOLUME_CONTROL,
470                                     BluetoothProfile.CONNECTION_POLICY_ALLOWED);
471                 }
472             } else {
473                 debugLog("clear VCP priority because dual mode is disabled by default");
474                 mAdapterService
475                         .getDatabase()
476                         .setProfileConnectionPolicy(
477                                 device,
478                                 BluetoothProfile.VOLUME_CONTROL,
479                                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
480             }
481         }
482 
483         // If we do not have a stored priority for HFP/A2DP (all roles) then default to on.
484         if ((panService != null)
485                 && (Utils.arrayContains(uuids, BluetoothUuid.PANU)
486                         && (panService.getConnectionPolicy(device)
487                                 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)
488                         && mAdapterService
489                                 .getResources()
490                                 .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) {
491             if (mAutoConnectProfilesSupported) {
492                 panService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
493             } else {
494                 mAdapterService
495                         .getDatabase()
496                         .setProfileConnectionPolicy(
497                                 device,
498                                 BluetoothProfile.PAN,
499                                 BluetoothProfile.CONNECTION_POLICY_ALLOWED);
500             }
501         }
502 
503         if ((leAudioService != null)
504                 && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO)
505                 && (leAudioService.getConnectionPolicy(device)
506                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
507             if (isLeAudioProfileAllowed) {
508                 debugLog("setting le audio profile priority for device " + device);
509                 if (mAutoConnectProfilesSupported) {
510                     leAudioService.setConnectionPolicy(
511                             device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
512                 } else {
513                     mAdapterService
514                             .getDatabase()
515                             .setProfileConnectionPolicy(
516                                     device,
517                                     BluetoothProfile.LE_AUDIO,
518                                     BluetoothProfile.CONNECTION_POLICY_ALLOWED);
519                 }
520             } else {
521                 debugLog("clear LEA profile priority because LE audio is not allowed");
522                 mAdapterService
523                         .getDatabase()
524                         .setProfileConnectionPolicy(
525                                 device,
526                                 BluetoothProfile.LE_AUDIO,
527                                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
528             }
529         }
530 
531         if ((hearingAidService != null)
532                 && Utils.arrayContains(uuids, BluetoothUuid.HEARING_AID)
533                 && (hearingAidService.getConnectionPolicy(device)
534                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
535             if (isLeAudioProfileAllowed) {
536                 Log.i(TAG, "LE Audio preferred over ASHA for device " + device);
537                 mAdapterService
538                         .getDatabase()
539                         .setProfileConnectionPolicy(
540                                 device,
541                                 BluetoothProfile.HEARING_AID,
542                                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
543             } else {
544                 debugLog("setting hearing aid profile priority for device " + device);
545                 if (mAutoConnectProfilesSupported) {
546                     hearingAidService.setConnectionPolicy(
547                             device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
548                 } else {
549                     mAdapterService
550                             .getDatabase()
551                             .setProfileConnectionPolicy(
552                                     device,
553                                     BluetoothProfile.HEARING_AID,
554                                     BluetoothProfile.CONNECTION_POLICY_ALLOWED);
555                 }
556             }
557         }
558 
559         if ((hapClientService != null)
560                 && Utils.arrayContains(uuids, BluetoothUuid.HAS)
561                 && (hapClientService.getConnectionPolicy(device)
562                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
563             debugLog("setting hearing access profile priority for device " + device);
564             if (isLeAudioProfileAllowed) {
565                 if (mAutoConnectProfilesSupported) {
566                     hapClientService.setConnectionPolicy(
567                             device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
568                 } else {
569                     mAdapterService
570                             .getDatabase()
571                             .setProfileConnectionPolicy(
572                                     device,
573                                     BluetoothProfile.HAP_CLIENT,
574                                     BluetoothProfile.CONNECTION_POLICY_ALLOWED);
575                 }
576             } else {
577                 mAdapterService
578                         .getDatabase()
579                         .setProfileConnectionPolicy(
580                                 device,
581                                 BluetoothProfile.HAP_CLIENT,
582                                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
583             }
584         }
585 
586         if ((bcService != null)
587                 && Utils.arrayContains(uuids, BluetoothUuid.BASS)
588                 && (bcService.getConnectionPolicy(device)
589                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
590             if (isLeAudioProfileAllowed) {
591                 debugLog("setting broadcast assistant profile priority for device " + device);
592                 if (mAutoConnectProfilesSupported) {
593                     bcService.setConnectionPolicy(
594                             device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
595                 } else {
596                     mAdapterService
597                             .getDatabase()
598                             .setProfileConnectionPolicy(
599                                     device,
600                                     BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
601                                     BluetoothProfile.CONNECTION_POLICY_ALLOWED);
602                 }
603             } else {
604                 debugLog(
605                         "clear broadcast assistant profile priority if le audio profile is not"
606                                 + " allowed");
607                 mAdapterService
608                         .getDatabase()
609                         .setProfileConnectionPolicy(
610                                 device,
611                                 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
612                                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
613             }
614         }
615 
616         if ((batteryService != null)
617                 && Utils.arrayContains(uuids, BluetoothUuid.BATTERY)
618                 && (batteryService.getConnectionPolicy(device)
619                         == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
620             debugLog("setting battery profile priority for device " + device);
621             if (mAutoConnectProfilesSupported) {
622                 batteryService.setConnectionPolicy(
623                         device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
624             } else {
625                 mAdapterService
626                         .getDatabase()
627                         .setProfileConnectionPolicy(
628                                 device,
629                                 BluetoothProfile.BATTERY,
630                                 BluetoothProfile.CONNECTION_POLICY_ALLOWED);
631             }
632         }
633     }
634 
handleLeAudioOnlyDeviceAfterCsipConnect(BluetoothDevice device)635     void handleLeAudioOnlyDeviceAfterCsipConnect(BluetoothDevice device) {
636         debugLog("handleLeAudioOnlyDeviceAfterCsipConnect: " + device);
637 
638         LeAudioService leAudioService = mFactory.getLeAudioService();
639         if (leAudioService == null
640                 || (leAudioService.getConnectionPolicy(device)
641                         == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
642                 || !mAdapterService.isProfileSupported(device, BluetoothProfile.LE_AUDIO)) {
643             debugLog("handleLeAudioOnlyDeviceAfterCsipConnect: nothing to do for " + device);
644             return;
645         }
646 
647         List<BluetoothDevice> groupDevices = new ArrayList<>();
648         boolean isAnyOtherGroupMemberAlreadyAllowed = false;
649 
650         CsipSetCoordinatorService csipSetCoordinatorService =
651                 mFactory.getCsipSetCoordinatorService();
652         if (csipSetCoordinatorService != null) {
653             /* Since isLeAudioOnlyGroup return true it means csipSetCoordinatorService is valid */
654             groupDevices =
655                     csipSetCoordinatorService.getGroupDevicesOrdered(
656                             csipSetCoordinatorService.getGroupId(device, BluetoothUuid.CAP));
657 
658             if (Flags.leaudioQuickLeaudioToggleSwitchFix()) {
659                 for (BluetoothDevice dev : groupDevices) {
660                     if (leAudioService.getConnectionPolicy(dev)
661                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
662                         isAnyOtherGroupMemberAlreadyAllowed = true;
663                         break;
664                     }
665                 }
666             }
667         }
668 
669         boolean isLeAudio = isLeAudioOnlyGroup(device);
670         debugLog(
671                 "handleLeAudioOnlyDeviceAfterCsipConnect: isAnyOtherGroupMemberAlreadyAllowed = "
672                         + isAnyOtherGroupMemberAlreadyAllowed
673                         + ", isLeAudioOnlyGroup = "
674                         + isLeAudio);
675         if (!isAnyOtherGroupMemberAlreadyAllowed && !isLeAudio) {
676             /* Log no needed as above function will log on error. */
677             return;
678         }
679 
680         debugLog("handleLeAudioOnlyDeviceAfterCsipConnect: enabling LeAudioOnlyDevice");
681         for (BluetoothDevice dev : groupDevices) {
682             if (leAudioService.getConnectionPolicy(dev)
683                     != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
684                 /* Setting LeAudio service as allowed is sufficient,
685                  * because other LeAudio services e.g. VC will
686                  * be enabled by LeAudio service automatically.
687                  */
688                 debugLog("...." + dev);
689                 leAudioService.setConnectionPolicy(dev, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
690             }
691         }
692     }
693 
694     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
processProfileStateChanged( BluetoothDevice device, int profileId, int nextState, int prevState)695     private void processProfileStateChanged(
696             BluetoothDevice device, int profileId, int nextState, int prevState) {
697         debugLog(
698                 "processProfileStateChanged, device="
699                         + device
700                         + ", profile="
701                         + BluetoothProfile.getProfileName(profileId)
702                         + ", "
703                         + prevState
704                         + " -> "
705                         + nextState);
706         if (((profileId == BluetoothProfile.A2DP)
707                 || (profileId == BluetoothProfile.HEADSET)
708                 || (profileId == BluetoothProfile.LE_AUDIO)
709                 || (profileId == BluetoothProfile.CSIP_SET_COORDINATOR)
710                 || (profileId == BluetoothProfile.VOLUME_CONTROL)
711                 || (profileId == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT))) {
712             if (nextState == BluetoothProfile.STATE_CONNECTED) {
713                 switch (profileId) {
714                     case BluetoothProfile.A2DP:
715                         mA2dpRetrySet.remove(device);
716                         break;
717                     case BluetoothProfile.HEADSET:
718                         mHeadsetRetrySet.remove(device);
719                         break;
720                     case BluetoothProfile.CSIP_SET_COORDINATOR:
721                         handleLeAudioOnlyDeviceAfterCsipConnect(device);
722                         break;
723                 }
724                 connectOtherProfile(device);
725             }
726             if (nextState == BluetoothProfile.STATE_DISCONNECTED) {
727                 if (prevState == BluetoothProfile.STATE_CONNECTING
728                         || prevState == BluetoothProfile.STATE_DISCONNECTING) {
729                     mDatabaseManager.setDisconnection(device, profileId);
730                 }
731                 handleAllProfilesDisconnected(device);
732             }
733         }
734     }
735 
736     /**
737      * Updates the last connection date in the connection order database for the newly active device
738      * if connected to the A2DP profile. If this is a dual mode audio device (supports classic and
739      * LE Audio), LE Audio is made active, and {@link Utils#isDualModeAudioEnabled()} is false, A2DP
740      * and HFP will be disconnected.
741      *
742      * @param device is the device we just made the active device
743      */
processActiveDeviceChanged(BluetoothDevice device, int profileId)744     private void processActiveDeviceChanged(BluetoothDevice device, int profileId) {
745         debugLog(
746                 "processActiveDeviceChanged, device="
747                         + device
748                         + ", profile="
749                         + BluetoothProfile.getProfileName(profileId)
750                         + " isDualModeAudioEnabled="
751                         + isDualModeAudioEnabled());
752 
753         if (device == null) {
754             return;
755         }
756 
757         mDatabaseManager.setConnection(device, profileId);
758 
759         boolean isDualMode = isDualModeAudioEnabled();
760 
761         if (profileId == BluetoothProfile.LE_AUDIO) {
762             A2dpService a2dpService = mFactory.getA2dpService();
763             HeadsetService hsService = mFactory.getHeadsetService();
764             LeAudioService leAudioService = mFactory.getLeAudioService();
765             HearingAidService hearingAidService = mFactory.getHearingAidService();
766 
767             if (leAudioService == null) {
768                 debugLog("processActiveDeviceChanged: LeAudioService is null");
769                 return;
770             }
771             List<BluetoothDevice> leAudioActiveGroupDevices =
772                     leAudioService.getGroupDevices(leAudioService.getGroupId(device));
773 
774             // Disable classic audio profiles and ASHA for all group devices as lead can change
775             for (BluetoothDevice activeGroupDevice : leAudioActiveGroupDevices) {
776                 if (hsService != null && !isDualMode) {
777                     debugLog(
778                             "Disable HFP for the LE audio dual mode group device "
779                                     + activeGroupDevice);
780                     hsService.setConnectionPolicy(
781                             activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
782                 }
783                 if (a2dpService != null && !isDualMode) {
784                     debugLog(
785                             "Disable A2DP for the LE audio dual mode group device "
786                                     + activeGroupDevice);
787                     a2dpService.setConnectionPolicy(
788                             activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
789                 }
790                 if (hearingAidService != null) {
791                     debugLog(
792                             "Disable ASHA for the LE audio dual mode group device "
793                                     + activeGroupDevice);
794                     hearingAidService.setConnectionPolicy(
795                             activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
796                 }
797             }
798         }
799     }
800 
processDeviceConnected(BluetoothDevice device)801     private void processDeviceConnected(BluetoothDevice device) {
802         debugLog("processDeviceConnected, device=" + device);
803         mDatabaseManager.setConnection(device);
804     }
805 
806     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
handleAllProfilesDisconnected(BluetoothDevice device)807     private boolean handleAllProfilesDisconnected(BluetoothDevice device) {
808         boolean atLeastOneProfileConnectedForDevice = false;
809         boolean allProfilesEmpty = true;
810         HeadsetService hsService = mFactory.getHeadsetService();
811         A2dpService a2dpService = mFactory.getA2dpService();
812         PanService panService = mFactory.getPanService();
813         LeAudioService leAudioService = mFactory.getLeAudioService();
814         CsipSetCoordinatorService csipSetCooridnatorService =
815                 mFactory.getCsipSetCoordinatorService();
816 
817         if (hsService != null) {
818             List<BluetoothDevice> hsConnDevList = hsService.getConnectedDevices();
819             allProfilesEmpty &= hsConnDevList.isEmpty();
820             atLeastOneProfileConnectedForDevice |= hsConnDevList.contains(device);
821         }
822         if (a2dpService != null) {
823             List<BluetoothDevice> a2dpConnDevList = a2dpService.getConnectedDevices();
824             allProfilesEmpty &= a2dpConnDevList.isEmpty();
825             atLeastOneProfileConnectedForDevice |= a2dpConnDevList.contains(device);
826         }
827         if (csipSetCooridnatorService != null) {
828             List<BluetoothDevice> csipConnDevList = csipSetCooridnatorService.getConnectedDevices();
829             allProfilesEmpty &= csipConnDevList.isEmpty();
830             atLeastOneProfileConnectedForDevice |= csipConnDevList.contains(device);
831         }
832         if (panService != null) {
833             List<BluetoothDevice> panConnDevList = panService.getConnectedDevices();
834             allProfilesEmpty &= panConnDevList.isEmpty();
835             atLeastOneProfileConnectedForDevice |= panConnDevList.contains(device);
836         }
837         if (leAudioService != null) {
838             List<BluetoothDevice> leAudioConnDevList = leAudioService.getConnectedDevices();
839             allProfilesEmpty &= leAudioConnDevList.isEmpty();
840             atLeastOneProfileConnectedForDevice |= leAudioConnDevList.contains(device);
841         }
842 
843         if (!atLeastOneProfileConnectedForDevice) {
844             // Consider this device as fully disconnected, don't bother connecting others
845             debugLog("handleAllProfilesDisconnected: all profiles disconnected for " + device);
846             mHeadsetRetrySet.remove(device);
847             mA2dpRetrySet.remove(device);
848             if (allProfilesEmpty) {
849                 debugLog(
850                         "handleAllProfilesDisconnected: all profiles disconnected for all"
851                                 + " devices");
852                 // reset retry status so that in the next round we can start retrying connections
853                 resetStates();
854             }
855             return true;
856         }
857         return false;
858     }
859 
resetStates()860     private void resetStates() {
861         mHeadsetRetrySet.clear();
862         mA2dpRetrySet.clear();
863     }
864 
865     @VisibleForTesting
866     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
autoConnect()867     void autoConnect() {
868         if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
869             Log.e(TAG, "autoConnect: BT is not ON. Exiting autoConnect");
870             return;
871         }
872         if (mAdapterService.isQuietModeEnabled()) {
873             Log.i(TAG, "autoConnect() - BT is in quiet mode. Not initiating autoConnect");
874             return;
875         }
876 
877         final BluetoothDevice mostRecentlyActiveA2dpDevice =
878                 mDatabaseManager.getMostRecentlyConnectedA2dpDevice();
879         if (mostRecentlyActiveA2dpDevice != null) {
880             debugLog(
881                     "autoConnect: Device "
882                             + mostRecentlyActiveA2dpDevice
883                             + " attempting auto connection");
884             autoConnectHeadset(mostRecentlyActiveA2dpDevice);
885             autoConnectA2dp(mostRecentlyActiveA2dpDevice);
886             autoConnectHidHost(mostRecentlyActiveA2dpDevice);
887             return;
888         }
889 
890         if (!Flags.autoConnectOnHfpWhenNoA2dpDevice()) {
891             debugLog("HFP auto connect is not enabled");
892             return;
893         }
894 
895         if (Flags.autoConnectOnMultipleHfpWhenNoA2dpDevice()) {
896             final List<BluetoothDevice> mostRecentlyConnectedHfpDevices =
897                     mDatabaseManager.getMostRecentlyActiveHfpDevices();
898             for (BluetoothDevice hfpDevice : mostRecentlyConnectedHfpDevices) {
899                 debugLog("autoConnect: Headset device: " + hfpDevice);
900                 autoConnectHeadset(hfpDevice);
901             }
902             if (mostRecentlyConnectedHfpDevices.size() == 0) {
903                 Log.i(TAG, "autoConnect: No device to reconnect to");
904             }
905             return;
906         }
907         debugLog("HFP multi auto connect is not enabled");
908 
909         // Try to autoConnect with Hfp only if there was no a2dp valid device
910         final BluetoothDevice mostRecentlyConnectedHfpDevice =
911                 mDatabaseManager.getMostRecentlyActiveHfpDevice();
912         if (mostRecentlyConnectedHfpDevice != null) {
913             debugLog("autoConnect: Headset device: " + mostRecentlyConnectedHfpDevice);
914             autoConnectHeadset(mostRecentlyConnectedHfpDevice);
915             return;
916         }
917         Log.i(TAG, "autoConnect: No device to reconnect to");
918     }
919 
autoConnectA2dp(BluetoothDevice device)920     private void autoConnectA2dp(BluetoothDevice device) {
921         final A2dpService a2dpService = mFactory.getA2dpService();
922         if (a2dpService == null) {
923             warnLog("autoConnectA2dp: service is null, failed to connect to " + device);
924             return;
925         }
926         int a2dpConnectionPolicy = a2dpService.getConnectionPolicy(device);
927         if (a2dpConnectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
928             debugLog("autoConnectA2dp: connecting A2DP with " + device);
929             a2dpService.connect(device);
930         } else {
931             debugLog(
932                     "autoConnectA2dp: skipped auto-connect A2DP with device "
933                             + device
934                             + " connectionPolicy "
935                             + a2dpConnectionPolicy);
936         }
937     }
938 
939     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
autoConnectHeadset(BluetoothDevice device)940     private void autoConnectHeadset(BluetoothDevice device) {
941         final HeadsetService hsService = mFactory.getHeadsetService();
942         if (hsService == null) {
943             warnLog("autoConnectHeadset: service is null, failed to connect to " + device);
944             return;
945         }
946         int headsetConnectionPolicy = hsService.getConnectionPolicy(device);
947         if (headsetConnectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
948             debugLog("autoConnectHeadset: Connecting HFP with " + device);
949             hsService.connect(device);
950         } else {
951             debugLog(
952                     "autoConnectHeadset: skipped auto-connect HFP with device "
953                             + device
954                             + " connectionPolicy "
955                             + headsetConnectionPolicy);
956         }
957     }
958 
959     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
autoConnectHidHost(BluetoothDevice device)960     private void autoConnectHidHost(BluetoothDevice device) {
961         final HidHostService hidHostService = mFactory.getHidHostService();
962         if (hidHostService == null) {
963             warnLog("autoConnectHidHost: service is null, failed to connect to " + device);
964             return;
965         }
966         int hidHostConnectionPolicy = hidHostService.getConnectionPolicy(device);
967         if (hidHostConnectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
968             debugLog("autoConnectHidHost: Connecting HID with " + device);
969             hidHostService.connect(device);
970         } else {
971             debugLog(
972                     "autoConnectHidHost: skipped auto-connect HID with device "
973                             + device
974                             + " connectionPolicy "
975                             + hidHostConnectionPolicy);
976         }
977     }
978 
connectOtherProfile(BluetoothDevice device)979     private void connectOtherProfile(BluetoothDevice device) {
980         if (mAdapterService.isQuietModeEnabled()) {
981             debugLog("connectOtherProfile: in quiet mode, skip connect other profile " + device);
982             return;
983         }
984         if (mConnectOtherProfilesDeviceSet.contains(device)) {
985             debugLog("connectOtherProfile: already scheduled callback for " + device);
986             return;
987         }
988         mConnectOtherProfilesDeviceSet.add(device);
989         Message m = mHandler.obtainMessage(MESSAGE_CONNECT_OTHER_PROFILES);
990         m.obj = device;
991         mHandler.sendMessageDelayed(m, sConnectOtherProfilesTimeoutMillis);
992     }
993 
994     // This function is called whenever a profile is connected.  This allows any other bluetooth
995     // profiles which are not already connected or in the process of connecting to attempt to
996     // connect to the device that initiated the connection.  In the event that this function is
997     // invoked and there are no current bluetooth connections no new profiles will be connected.
998     @RequiresPermission(
999             allOf = {
1000                 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1001                 android.Manifest.permission.MODIFY_PHONE_STATE,
1002             })
processConnectOtherProfiles(BluetoothDevice device)1003     private void processConnectOtherProfiles(BluetoothDevice device) {
1004         debugLog("processConnectOtherProfiles, device=" + device);
1005         if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
1006             warnLog("processConnectOtherProfiles, adapter is not ON " + mAdapterService.getState());
1007             return;
1008         }
1009 
1010         /* Make sure that device is still connected before connecting other profiles */
1011         if (mAdapterService.getConnectionState(device)
1012                 == BluetoothDevice.CONNECTION_STATE_DISCONNECTED) {
1013             debugLog("processConnectOtherProfiles: device is not connected anymore " + device);
1014             return;
1015         }
1016 
1017         if (handleAllProfilesDisconnected(device)) {
1018             debugLog("processConnectOtherProfiles: all profiles disconnected for " + device);
1019             return;
1020         }
1021 
1022         HeadsetService hsService = mFactory.getHeadsetService();
1023         A2dpService a2dpService = mFactory.getA2dpService();
1024         PanService panService = mFactory.getPanService();
1025         LeAudioService leAudioService = mFactory.getLeAudioService();
1026         CsipSetCoordinatorService csipSetCooridnatorService =
1027                 mFactory.getCsipSetCoordinatorService();
1028         VolumeControlService volumeControlService = mFactory.getVolumeControlService();
1029         BatteryService batteryService = mFactory.getBatteryService();
1030         HidHostService hidHostService = mFactory.getHidHostService();
1031         BassClientService bcService = mFactory.getBassClientService();
1032 
1033         if (hsService != null) {
1034             if (!mHeadsetRetrySet.contains(device)
1035                     && (hsService.getConnectionPolicy(device)
1036                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1037                     && (hsService.getConnectionState(device)
1038                             == BluetoothProfile.STATE_DISCONNECTED)) {
1039                 debugLog("Retrying connection to Headset with device " + device);
1040                 mHeadsetRetrySet.add(device);
1041                 hsService.connect(device);
1042             }
1043         }
1044         if (a2dpService != null) {
1045             if (!mA2dpRetrySet.contains(device)
1046                     && (a2dpService.getConnectionPolicy(device)
1047                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1048                     && (a2dpService.getConnectionState(device)
1049                             == BluetoothProfile.STATE_DISCONNECTED)) {
1050                 debugLog("Retrying connection to A2DP with device " + device);
1051                 mA2dpRetrySet.add(device);
1052                 a2dpService.connect(device);
1053             }
1054         }
1055         if (panService != null) {
1056             List<BluetoothDevice> panConnDevList = panService.getConnectedDevices();
1057             // TODO: the panConnDevList.isEmpty() check below should be removed once
1058             // Multi-PAN is supported.
1059             if (panConnDevList.isEmpty()
1060                     && (panService.getConnectionPolicy(device)
1061                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1062                     && (panService.getConnectionState(device)
1063                             == BluetoothProfile.STATE_DISCONNECTED)) {
1064                 debugLog("Retrying connection to PAN with device " + device);
1065                 panService.connect(device);
1066             }
1067         }
1068         if (leAudioService != null) {
1069             List<BluetoothDevice> leAudioConnDevList = leAudioService.getConnectedDevices();
1070             if (!leAudioConnDevList.contains(device)
1071                     && (leAudioService.getConnectionPolicy(device)
1072                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1073                     && (leAudioService.getConnectionState(device)
1074                             == BluetoothProfile.STATE_DISCONNECTED)) {
1075                 debugLog("Retrying connection to LEAudio with device " + device);
1076                 leAudioService.connect(device);
1077             }
1078         }
1079         if (csipSetCooridnatorService != null) {
1080             List<BluetoothDevice> csipConnDevList = csipSetCooridnatorService.getConnectedDevices();
1081             if (!csipConnDevList.contains(device)
1082                     && (csipSetCooridnatorService.getConnectionPolicy(device)
1083                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1084                     && (csipSetCooridnatorService.getConnectionState(device)
1085                             == BluetoothProfile.STATE_DISCONNECTED)) {
1086                 debugLog("Retrying connection to CSIP with device " + device);
1087                 csipSetCooridnatorService.connect(device);
1088             }
1089         }
1090         if (volumeControlService != null) {
1091             List<BluetoothDevice> vcConnDevList = volumeControlService.getConnectedDevices();
1092             if (!vcConnDevList.contains(device)
1093                     && (volumeControlService.getConnectionPolicy(device)
1094                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1095                     && (volumeControlService.getConnectionState(device)
1096                             == BluetoothProfile.STATE_DISCONNECTED)) {
1097                 debugLog("Retrying connection to VCP with device " + device);
1098                 volumeControlService.connect(device);
1099             }
1100         }
1101         if (batteryService != null) {
1102             List<BluetoothDevice> connectedDevices = batteryService.getConnectedDevices();
1103             if (!connectedDevices.contains(device)
1104                     && (batteryService.getConnectionPolicy(device)
1105                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1106                     && (batteryService.getConnectionState(device)
1107                             == BluetoothProfile.STATE_DISCONNECTED)) {
1108                 debugLog("Retrying connection to BAS with device " + device);
1109                 batteryService.connect(device);
1110             }
1111         }
1112         if (hidHostService != null) {
1113             if ((hidHostService.getConnectionPolicy(device)
1114                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1115                     && (hidHostService.getConnectionState(device)
1116                             == BluetoothProfile.STATE_DISCONNECTED)) {
1117                 debugLog("Retrying connection to HID with device " + device);
1118                 hidHostService.connect(device);
1119             }
1120         }
1121         if (bcService != null) {
1122             List<BluetoothDevice> connectedDevices = bcService.getConnectedDevices();
1123             if (!connectedDevices.contains(device)
1124                     && (bcService.getConnectionPolicy(device)
1125                             == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
1126                     && (bcService.getConnectionState(device)
1127                             == BluetoothProfile.STATE_DISCONNECTED)) {
1128                 debugLog("Retrying connection to BASS with device " + device);
1129                 bcService.connect(device);
1130             }
1131         }
1132     }
1133 
1134     /**
1135      * Direct call prior to sending out {@link BluetoothDevice#ACTION_UUID}. This indicates that
1136      * service discovery is complete and passes the UUIDs directly to PhonePolicy.
1137      *
1138      * @param device is the remote device whose services have been discovered
1139      * @param uuids are the services supported by the remote device
1140      */
onUuidsDiscovered(BluetoothDevice device, ParcelUuid[] uuids)1141     void onUuidsDiscovered(BluetoothDevice device, ParcelUuid[] uuids) {
1142         debugLog("onUuidsDiscovered: discovered services for device " + device);
1143         if (uuids != null) {
1144             processInitProfilePriorities(device, uuids);
1145         } else {
1146             warnLog("onUuidsDiscovered: uuids is null for device " + device);
1147         }
1148     }
1149 
debugLog(String msg)1150     private static void debugLog(String msg) {
1151         Log.d(TAG, msg);
1152     }
1153 
warnLog(String msg)1154     private static void warnLog(String msg) {
1155         Log.w(TAG, msg);
1156     }
1157 }
1158