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