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