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