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.BluetoothA2dp; 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothCsipSetCoordinator; 25 import android.bluetooth.BluetoothDevice; 26 import android.bluetooth.BluetoothHeadset; 27 import android.bluetooth.BluetoothHearingAid; 28 import android.bluetooth.BluetoothLeAudio; 29 import android.bluetooth.BluetoothProfile; 30 import android.bluetooth.BluetoothUuid; 31 import android.bluetooth.BluetoothVolumeControl; 32 import android.content.BroadcastReceiver; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.IntentFilter; 36 import android.os.Handler; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.ParcelUuid; 40 import android.os.SystemProperties; 41 import android.provider.DeviceConfig; 42 import android.util.Log; 43 44 import com.android.bluetooth.R; 45 import com.android.bluetooth.Utils; 46 import com.android.bluetooth.a2dp.A2dpService; 47 import com.android.bluetooth.bas.BatteryService; 48 import com.android.bluetooth.bass_client.BassClientService; 49 import com.android.bluetooth.btservice.storage.DatabaseManager; 50 import com.android.bluetooth.csip.CsipSetCoordinatorService; 51 import com.android.bluetooth.hap.HapClientService; 52 import com.android.bluetooth.hearingaid.HearingAidService; 53 import com.android.bluetooth.hfp.HeadsetService; 54 import com.android.bluetooth.hid.HidHostService; 55 import com.android.bluetooth.le_audio.LeAudioService; 56 import com.android.bluetooth.pan.PanService; 57 import com.android.bluetooth.vc.VolumeControlService; 58 import com.android.internal.annotations.VisibleForTesting; 59 60 import java.util.HashSet; 61 import java.util.List; 62 import java.util.Objects; 63 64 // Describes the phone policy 65 // 66 // The policy should be as decoupled from the stack as possible. In an ideal world we should not 67 // need to have this policy talk with any non-public APIs and one way to enforce that would be to 68 // keep this file outside the Bluetooth process. Unfortunately, keeping a separate process alive is 69 // an expensive and a tedious task. 70 // 71 // Best practices: 72 // a) PhonePolicy should be ALL private methods 73 // -- Use broadcasts which can be listened in on the BroadcastReceiver 74 // b) NEVER call from the PhonePolicy into the Java stack, unless public APIs. It is OK to call into 75 // the non public versions as long as public versions exist (so that a 3rd party policy can mimick) 76 // us. 77 // 78 // Policy description: 79 // 80 // Policies are usually governed by outside events that may warrant an action. We talk about various 81 // events and the resulting outcome from this policy: 82 // 83 // 1. Adapter turned ON: At this point we will try to auto-connect the (device, profile) pairs which 84 // have PRIORITY_AUTO_CONNECT. The fact that we *only* auto-connect Headset and A2DP is something 85 // that is hardcoded and specific to phone policy (see autoConnect() function) 86 // 2. When the profile connection-state changes: At this point if a new profile gets CONNECTED we 87 // will try to connect other profiles on the same device. This is to avoid collision if devices 88 // somehow end up trying to connect at same time or general connection issues. 89 class PhonePolicy { 90 private static final boolean DBG = true; 91 private static final String TAG = "BluetoothPhonePolicy"; 92 93 // Message types for the handler (internal messages generated by intents or timeouts) 94 private static final int MESSAGE_PROFILE_CONNECTION_STATE_CHANGED = 1; 95 private static final int MESSAGE_CONNECT_OTHER_PROFILES = 3; 96 private static final int MESSAGE_ADAPTER_STATE_TURNED_ON = 4; 97 private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5; 98 private static final int MESSAGE_DEVICE_CONNECTED = 6; 99 100 @VisibleForTesting static final String AUTO_CONNECT_PROFILES_PROPERTY = 101 "bluetooth.auto_connect_profiles.enabled"; 102 private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default"; 103 104 private static boolean sLeAudioEnabledByDefault = DeviceConfig.getBoolean( 105 DeviceConfig.NAMESPACE_BLUETOOTH, CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false); 106 107 // Timeouts 108 @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s 109 110 private DatabaseManager mDatabaseManager; 111 private final AdapterService mAdapterService; 112 private final ServiceFactory mFactory; 113 private final Handler mHandler; 114 private final HashSet<BluetoothDevice> mHeadsetRetrySet = new HashSet<>(); 115 private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>(); 116 private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>(); 117 @VisibleForTesting boolean mAutoConnectProfilesSupported; 118 119 // Broadcast receiver for all changes to states of various profiles 120 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 121 @Override 122 public void onReceive(Context context, Intent intent) { 123 String action = intent.getAction(); 124 if (action == null) { 125 errorLog("Received intent with null action"); 126 return; 127 } 128 switch (action) { 129 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: 130 mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED, 131 BluetoothProfile.HEADSET, -1, // No-op argument 132 intent).sendToTarget(); 133 break; 134 case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: 135 mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED, 136 BluetoothProfile.A2DP, -1, // No-op argument 137 intent).sendToTarget(); 138 break; 139 case BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED: 140 mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED, 141 BluetoothProfile.CSIP_SET_COORDINATOR, -1, // No-op argument 142 intent).sendToTarget(); 143 break; 144 case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: 145 mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED, 146 BluetoothProfile.LE_AUDIO, -1, // No-op argument 147 intent).sendToTarget(); 148 break; 149 case BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED: 150 mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED, 151 BluetoothProfile.VOLUME_CONTROL, -1, // No-op argument 152 intent).sendToTarget(); 153 break; 154 case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED: 155 mHandler.obtainMessage(MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED, 156 BluetoothProfile.A2DP, -1, // No-op argument 157 intent).sendToTarget(); 158 break; 159 case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED: 160 mHandler.obtainMessage(MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED, 161 BluetoothProfile.HEADSET, -1, // No-op argument 162 intent).sendToTarget(); 163 break; 164 case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED: 165 mHandler.obtainMessage(MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED, 166 BluetoothProfile.HEARING_AID, -1, // No-op argument 167 intent).sendToTarget(); 168 break; 169 case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED: 170 mHandler.obtainMessage(MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED, 171 BluetoothProfile.LE_AUDIO, -1, // No-op argument 172 intent).sendToTarget(); 173 break; 174 case BluetoothAdapter.ACTION_STATE_CHANGED: 175 // Only pass the message on if the adapter has actually changed state from 176 // non-ON to ON. NOTE: ON is the state depicting BREDR ON and not just BLE ON. 177 int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); 178 if (newState == BluetoothAdapter.STATE_ON) { 179 mHandler.obtainMessage(MESSAGE_ADAPTER_STATE_TURNED_ON).sendToTarget(); 180 } 181 break; 182 case BluetoothDevice.ACTION_ACL_CONNECTED: 183 mHandler.obtainMessage(MESSAGE_DEVICE_CONNECTED, intent).sendToTarget(); 184 break; 185 default: 186 Log.e(TAG, "Received unexpected intent, action=" + action); 187 break; 188 } 189 } 190 }; 191 192 @VisibleForTesting getBroadcastReceiver()193 BroadcastReceiver getBroadcastReceiver() { 194 return mReceiver; 195 } 196 197 // Handler to handoff intents to class thread 198 class PhonePolicyHandler extends Handler { PhonePolicyHandler(Looper looper)199 PhonePolicyHandler(Looper looper) { 200 super(looper); 201 } 202 203 @Override handleMessage(Message msg)204 public void handleMessage(Message msg) { 205 switch (msg.what) { 206 case MESSAGE_PROFILE_CONNECTION_STATE_CHANGED: { 207 Intent intent = (Intent) msg.obj; 208 BluetoothDevice device = 209 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 210 int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); 211 int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 212 processProfileStateChanged(device, msg.arg1, nextState, prevState); 213 } 214 break; 215 216 case MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED: { 217 Intent intent = (Intent) msg.obj; 218 BluetoothDevice activeDevice = 219 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 220 processActiveDeviceChanged(activeDevice, msg.arg1); 221 } 222 break; 223 224 case MESSAGE_CONNECT_OTHER_PROFILES: { 225 // Called when we try connect some profiles in processConnectOtherProfiles but 226 // we send a delayed message to try connecting the remaining profiles 227 BluetoothDevice device = (BluetoothDevice) msg.obj; 228 processConnectOtherProfiles(device); 229 mConnectOtherProfilesDeviceSet.remove(device); 230 break; 231 } 232 case MESSAGE_ADAPTER_STATE_TURNED_ON: 233 // Call auto connect when adapter switches state to ON 234 resetStates(); 235 autoConnect(); 236 break; 237 case MESSAGE_DEVICE_CONNECTED: 238 Intent intent = (Intent) msg.obj; 239 BluetoothDevice device = 240 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 241 processDeviceConnected(device); 242 } 243 } 244 } 245 246 ; 247 248 // Policy API functions for lifecycle management (protected) start()249 protected void start() { 250 IntentFilter filter = new IntentFilter(); 251 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 252 filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 253 filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 254 filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); 255 filter.addAction(BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED); 256 filter.addAction(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED); 257 filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); 258 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 259 filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); 260 filter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 261 filter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); 262 filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); 263 mAdapterService.registerReceiver(mReceiver, filter); 264 } 265 cleanup()266 protected void cleanup() { 267 mAdapterService.unregisterReceiver(mReceiver); 268 resetStates(); 269 } 270 PhonePolicy(AdapterService service, ServiceFactory factory)271 PhonePolicy(AdapterService service, ServiceFactory factory) { 272 mAdapterService = service; 273 mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), 274 "DatabaseManager cannot be null when PhonePolicy starts"); 275 mFactory = factory; 276 mHandler = new PhonePolicyHandler(service.getMainLooper()); 277 mAutoConnectProfilesSupported = SystemProperties.getBoolean( 278 AUTO_CONNECT_PROFILES_PROPERTY, false); 279 } 280 281 // Policy implementation, all functions MUST be private 282 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) processInitProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids)283 private void processInitProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids) { 284 debugLog("processInitProfilePriorities() - device " + device); 285 HidHostService hidService = mFactory.getHidHostService(); 286 A2dpService a2dpService = mFactory.getA2dpService(); 287 HeadsetService headsetService = mFactory.getHeadsetService(); 288 PanService panService = mFactory.getPanService(); 289 HearingAidService hearingAidService = mFactory.getHearingAidService(); 290 LeAudioService leAudioService = mFactory.getLeAudioService(); 291 CsipSetCoordinatorService csipSetCoordinatorService = 292 mFactory.getCsipSetCoordinatorService(); 293 VolumeControlService volumeControlService = 294 mFactory.getVolumeControlService(); 295 HapClientService hapClientService = mFactory.getHapClientService(); 296 BassClientService bcService = mFactory.getBassClientService(); 297 BatteryService batteryService = mFactory.getBatteryService(); 298 299 boolean isLeAudioProfileAllowed = (leAudioService != null) 300 && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO) 301 && (leAudioService.getConnectionPolicy(device) 302 != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) 303 && mAdapterService.isLeAudioAllowed(device) 304 && (sLeAudioEnabledByDefault || isDualModeAudioEnabled()); 305 306 // Set profile priorities only for the profiles discovered on the remote device. 307 // This avoids needless auto-connect attempts to profiles non-existent on the remote device 308 if ((hidService != null) && (Utils.arrayContains(uuids, BluetoothUuid.HID) 309 || Utils.arrayContains(uuids, BluetoothUuid.HOGP)) && ( 310 hidService.getConnectionPolicy(device) 311 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 312 if (mAutoConnectProfilesSupported) { 313 hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 314 } else { 315 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 316 BluetoothProfile.HID_HOST, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 317 } 318 } 319 320 if ((headsetService != null) 321 && ((Utils.arrayContains(uuids, BluetoothUuid.HSP) 322 || Utils.arrayContains(uuids, BluetoothUuid.HFP)) 323 && (headsetService.getConnectionPolicy(device) 324 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN))) { 325 if (!isDualModeAudioEnabled() && isLeAudioProfileAllowed) { 326 debugLog("clear hfp profile priority for the le audio dual mode device " 327 + device); 328 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 329 BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 330 } else { 331 if (mAutoConnectProfilesSupported) { 332 headsetService.setConnectionPolicy(device, 333 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 334 } else { 335 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 336 BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 337 } 338 } 339 } 340 341 if ((a2dpService != null) 342 && (Utils.arrayContains(uuids, BluetoothUuid.A2DP_SINK) 343 || Utils.arrayContains(uuids, BluetoothUuid.ADV_AUDIO_DIST)) 344 && (a2dpService.getConnectionPolicy(device) 345 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 346 if (!isDualModeAudioEnabled() && isLeAudioProfileAllowed) { 347 debugLog("clear a2dp profile priority for the le audio dual mode device " 348 + device); 349 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 350 BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 351 } else { 352 if (mAutoConnectProfilesSupported) { 353 a2dpService.setConnectionPolicy(device, 354 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 355 } else { 356 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 357 BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 358 } 359 } 360 } 361 362 // CSIP should be connected prior to LE Audio 363 if ((csipSetCoordinatorService != null) 364 && (Utils.arrayContains(uuids, BluetoothUuid.COORDINATED_SET)) 365 && (csipSetCoordinatorService.getConnectionPolicy(device) 366 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 367 // Always allow CSIP during pairing process regardless of LE audio preference 368 if (mAutoConnectProfilesSupported) { 369 csipSetCoordinatorService.setConnectionPolicy(device, 370 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 371 } else { 372 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 373 BluetoothProfile.CSIP_SET_COORDINATOR, 374 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 375 } 376 } 377 378 // If we do not have a stored priority for HFP/A2DP (all roles) then default to on. 379 if ((panService != null) 380 && (Utils.arrayContains(uuids, BluetoothUuid.PANU) 381 && (panService.getConnectionPolicy(device) 382 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN) 383 && mAdapterService.getResources() 384 .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) { 385 if (mAutoConnectProfilesSupported) { 386 panService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 387 } else { 388 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 389 BluetoothProfile.PAN, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 390 } 391 } 392 393 if ((leAudioService != null) 394 && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO) 395 && (leAudioService.getConnectionPolicy(device) 396 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 397 if (isLeAudioProfileAllowed) { 398 debugLog("setting le audio profile priority for device " + device); 399 if (mAutoConnectProfilesSupported) { 400 leAudioService.setConnectionPolicy(device, 401 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 402 } else { 403 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 404 BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 405 } 406 } else { 407 debugLog("clear LEA profile priority because LE audio is not allowed"); 408 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 409 BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 410 } 411 } 412 413 if ((hearingAidService != null) 414 && Utils.arrayContains(uuids, BluetoothUuid.HEARING_AID) 415 && (hearingAidService.getConnectionPolicy(device) 416 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 417 if (isLeAudioProfileAllowed) { 418 debugLog("LE Audio preferred over ASHA for device " + device); 419 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 420 BluetoothProfile.HEARING_AID, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 421 } else { 422 debugLog("setting hearing aid profile priority for device " + device); 423 if (mAutoConnectProfilesSupported) { 424 hearingAidService.setConnectionPolicy(device, 425 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 426 } else { 427 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 428 BluetoothProfile.HEARING_AID, 429 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 430 } 431 } 432 } 433 434 if ((volumeControlService != null) 435 && Utils.arrayContains(uuids, BluetoothUuid.VOLUME_CONTROL) 436 && (volumeControlService.getConnectionPolicy(device) 437 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 438 if (isLeAudioProfileAllowed) { 439 debugLog("setting volume control profile priority for device " + device); 440 if (mAutoConnectProfilesSupported) { 441 volumeControlService.setConnectionPolicy(device, 442 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 443 } else { 444 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 445 BluetoothProfile.VOLUME_CONTROL, 446 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 447 } 448 } else { 449 debugLog("clear VCP priority because dual mode is disabled by default"); 450 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 451 BluetoothProfile.VOLUME_CONTROL, 452 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 453 } 454 } 455 456 if ((hapClientService != null) 457 && Utils.arrayContains(uuids, BluetoothUuid.HAS) 458 && (hapClientService.getConnectionPolicy(device) 459 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 460 debugLog("setting hearing access profile priority for device " + device); 461 if (isLeAudioProfileAllowed) { 462 if (mAutoConnectProfilesSupported) { 463 hapClientService.setConnectionPolicy(device, 464 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 465 } else { 466 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 467 BluetoothProfile.HAP_CLIENT, 468 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 469 } 470 } else { 471 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 472 BluetoothProfile.HAP_CLIENT, 473 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 474 } 475 } 476 477 if ((bcService != null) 478 && Utils.arrayContains(uuids, BluetoothUuid.BASS) 479 && (bcService.getConnectionPolicy(device) 480 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 481 debugLog("setting broadcast assistant profile priority for device " + device); 482 if (mAutoConnectProfilesSupported) { 483 bcService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 484 } else { 485 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 486 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, 487 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 488 } 489 } 490 491 if ((batteryService != null) 492 && Utils.arrayContains(uuids, BluetoothUuid.BATTERY) 493 && (batteryService.getConnectionPolicy(device) 494 == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { 495 debugLog("setting battery profile priority for device " + device); 496 if (mAutoConnectProfilesSupported) { 497 batteryService.setConnectionPolicy(device, 498 BluetoothProfile.CONNECTION_POLICY_ALLOWED); 499 } else { 500 mAdapterService.getDatabase().setProfileConnectionPolicy(device, 501 BluetoothProfile.BATTERY, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 502 } 503 } 504 } 505 506 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) processProfileStateChanged(BluetoothDevice device, int profileId, int nextState, int prevState)507 private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState, 508 int prevState) { 509 debugLog("processProfileStateChanged, device=" + device + ", profile=" + profileId + ", " 510 + prevState + " -> " + nextState); 511 if (((profileId == BluetoothProfile.A2DP) || (profileId == BluetoothProfile.HEADSET) 512 || (profileId == BluetoothProfile.LE_AUDIO) 513 || (profileId == BluetoothProfile.CSIP_SET_COORDINATOR) 514 || (profileId == BluetoothProfile.VOLUME_CONTROL) 515 || (profileId == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT))) { 516 if (nextState == BluetoothProfile.STATE_CONNECTED) { 517 switch (profileId) { 518 case BluetoothProfile.A2DP: 519 mA2dpRetrySet.remove(device); 520 break; 521 case BluetoothProfile.HEADSET: 522 mHeadsetRetrySet.remove(device); 523 break; 524 } 525 connectOtherProfile(device); 526 } 527 if (nextState == BluetoothProfile.STATE_DISCONNECTED) { 528 if (profileId == BluetoothProfile.A2DP 529 && (prevState == BluetoothProfile.STATE_CONNECTING 530 || prevState == BluetoothProfile.STATE_DISCONNECTING)) { 531 mDatabaseManager.setDisconnection(device); 532 } 533 handleAllProfilesDisconnected(device); 534 } 535 } 536 } 537 538 /** 539 * Updates the last connection date in the connection order database for the newly active device 540 * if connected to the A2DP profile. If this is a dual mode audio device (supports classic and 541 * LE Audio), LE Audio is made active, and {@link Utils#isDualModeAudioEnabled()} is false, 542 * A2DP and HFP will be disconnected. 543 * 544 * @param device is the device we just made the active device 545 */ processActiveDeviceChanged(BluetoothDevice device, int profileId)546 private void processActiveDeviceChanged(BluetoothDevice device, int profileId) { 547 debugLog("processActiveDeviceChanged, device=" + device + ", profile=" + profileId 548 + " isDualModeAudioEnabled=" + isDualModeAudioEnabled()); 549 550 if (device != null) { 551 mDatabaseManager.setConnection(device, profileId == BluetoothProfile.A2DP); 552 553 if (isDualModeAudioEnabled()) return; 554 if (profileId == BluetoothProfile.LE_AUDIO) { 555 A2dpService a2dpService = mFactory.getA2dpService(); 556 HeadsetService hsService = mFactory.getHeadsetService(); 557 LeAudioService leAudioService = mFactory.getLeAudioService(); 558 if (leAudioService == null) { 559 debugLog("processActiveDeviceChanged: LeAudioService is null"); 560 return; 561 } 562 List<BluetoothDevice> leAudioActiveGroupDevices = 563 leAudioService.getGroupDevices(leAudioService.getGroupId(device)); 564 565 // Disable classic audio profiles for all group devices as lead can change 566 for (BluetoothDevice activeGroupDevice: leAudioActiveGroupDevices) { 567 if (hsService != null) { 568 debugLog("Disable HFP for the LE audio dual mode group device " 569 + activeGroupDevice); 570 hsService.setConnectionPolicy(activeGroupDevice, 571 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 572 } 573 if (a2dpService != null) { 574 debugLog("Disable A2DP for the LE audio dual mode group device " 575 + activeGroupDevice); 576 a2dpService.setConnectionPolicy(activeGroupDevice, 577 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 578 } 579 } 580 } 581 } 582 } 583 processDeviceConnected(BluetoothDevice device)584 private void processDeviceConnected(BluetoothDevice device) { 585 debugLog("processDeviceConnected, device=" + device); 586 mDatabaseManager.setConnection(device, false); 587 } 588 589 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) handleAllProfilesDisconnected(BluetoothDevice device)590 private boolean handleAllProfilesDisconnected(BluetoothDevice device) { 591 boolean atLeastOneProfileConnectedForDevice = false; 592 boolean allProfilesEmpty = true; 593 HeadsetService hsService = mFactory.getHeadsetService(); 594 A2dpService a2dpService = mFactory.getA2dpService(); 595 PanService panService = mFactory.getPanService(); 596 LeAudioService leAudioService = mFactory.getLeAudioService(); 597 CsipSetCoordinatorService csipSetCooridnatorService = 598 mFactory.getCsipSetCoordinatorService(); 599 600 if (hsService != null) { 601 List<BluetoothDevice> hsConnDevList = hsService.getConnectedDevices(); 602 allProfilesEmpty &= hsConnDevList.isEmpty(); 603 atLeastOneProfileConnectedForDevice |= hsConnDevList.contains(device); 604 } 605 if (a2dpService != null) { 606 List<BluetoothDevice> a2dpConnDevList = a2dpService.getConnectedDevices(); 607 allProfilesEmpty &= a2dpConnDevList.isEmpty(); 608 atLeastOneProfileConnectedForDevice |= a2dpConnDevList.contains(device); 609 } 610 if (csipSetCooridnatorService != null) { 611 List<BluetoothDevice> csipConnDevList = csipSetCooridnatorService.getConnectedDevices(); 612 allProfilesEmpty &= csipConnDevList.isEmpty(); 613 atLeastOneProfileConnectedForDevice |= csipConnDevList.contains(device); 614 } 615 if (panService != null) { 616 List<BluetoothDevice> panConnDevList = panService.getConnectedDevices(); 617 allProfilesEmpty &= panConnDevList.isEmpty(); 618 atLeastOneProfileConnectedForDevice |= panConnDevList.contains(device); 619 } 620 if (leAudioService != null) { 621 List<BluetoothDevice> leAudioConnDevList = leAudioService.getConnectedDevices(); 622 allProfilesEmpty &= leAudioConnDevList.isEmpty(); 623 atLeastOneProfileConnectedForDevice |= leAudioConnDevList.contains(device); 624 } 625 626 if (!atLeastOneProfileConnectedForDevice) { 627 // Consider this device as fully disconnected, don't bother connecting others 628 debugLog("handleAllProfilesDisconnected: all profiles disconnected for " + device); 629 mHeadsetRetrySet.remove(device); 630 mA2dpRetrySet.remove(device); 631 if (allProfilesEmpty) { 632 debugLog("handleAllProfilesDisconnected: all profiles disconnected for all" 633 + " devices"); 634 // reset retry status so that in the next round we can start retrying connections 635 resetStates(); 636 } 637 return true; 638 } 639 return false; 640 } 641 resetStates()642 private void resetStates() { 643 mHeadsetRetrySet.clear(); 644 mA2dpRetrySet.clear(); 645 } 646 647 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) autoConnect()648 private void autoConnect() { 649 if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) { 650 errorLog("autoConnect: BT is not ON. Exiting autoConnect"); 651 return; 652 } 653 654 if (!mAdapterService.isQuietModeEnabled()) { 655 debugLog("autoConnect: Initiate auto connection on BT on..."); 656 final BluetoothDevice mostRecentlyActiveA2dpDevice = 657 mDatabaseManager.getMostRecentlyConnectedA2dpDevice(); 658 if (mostRecentlyActiveA2dpDevice == null) { 659 errorLog("autoConnect: most recently active a2dp device is null"); 660 return; 661 } 662 debugLog("autoConnect: Device " + mostRecentlyActiveA2dpDevice 663 + " attempting auto connection"); 664 autoConnectHeadset(mostRecentlyActiveA2dpDevice); 665 autoConnectA2dp(mostRecentlyActiveA2dpDevice); 666 autoConnectHidHost(mostRecentlyActiveA2dpDevice); 667 } else { 668 debugLog("autoConnect() - BT is in quiet mode. Not initiating auto connections"); 669 } 670 } 671 autoConnectA2dp(BluetoothDevice device)672 private void autoConnectA2dp(BluetoothDevice device) { 673 final A2dpService a2dpService = mFactory.getA2dpService(); 674 if (a2dpService == null) { 675 warnLog("autoConnectA2dp: service is null, failed to connect to " + device); 676 return; 677 } 678 int a2dpConnectionPolicy = a2dpService.getConnectionPolicy(device); 679 if (a2dpConnectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 680 debugLog("autoConnectA2dp: connecting A2DP with " + device); 681 a2dpService.connect(device); 682 } else { 683 debugLog("autoConnectA2dp: skipped auto-connect A2DP with device " + device 684 + " connectionPolicy " + a2dpConnectionPolicy); 685 } 686 } 687 688 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) autoConnectHeadset(BluetoothDevice device)689 private void autoConnectHeadset(BluetoothDevice device) { 690 final HeadsetService hsService = mFactory.getHeadsetService(); 691 if (hsService == null) { 692 warnLog("autoConnectHeadset: service is null, failed to connect to " + device); 693 return; 694 } 695 int headsetConnectionPolicy = hsService.getConnectionPolicy(device); 696 if (headsetConnectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 697 debugLog("autoConnectHeadset: Connecting HFP with " + device); 698 hsService.connect(device); 699 } else { 700 debugLog("autoConnectHeadset: skipped auto-connect HFP with device " + device 701 + " connectionPolicy " + headsetConnectionPolicy); 702 } 703 } 704 705 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) autoConnectHidHost(BluetoothDevice device)706 private void autoConnectHidHost(BluetoothDevice device) { 707 final HidHostService hidHostService = mFactory.getHidHostService(); 708 if (hidHostService == null) { 709 warnLog("autoConnectHidHost: service is null, failed to connect to " + device); 710 return; 711 } 712 int hidHostConnectionPolicy = hidHostService.getConnectionPolicy(device); 713 if (hidHostConnectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 714 debugLog("autoConnectHidHost: Connecting HID with " + device); 715 hidHostService.connect(device); 716 } else { 717 debugLog("autoConnectHidHost: skipped auto-connect HID with device " + device 718 + " connectionPolicy " + hidHostConnectionPolicy); 719 } 720 } 721 connectOtherProfile(BluetoothDevice device)722 private void connectOtherProfile(BluetoothDevice device) { 723 if (mAdapterService.isQuietModeEnabled()) { 724 debugLog("connectOtherProfile: in quiet mode, skip connect other profile " + device); 725 return; 726 } 727 if (mConnectOtherProfilesDeviceSet.contains(device)) { 728 debugLog("connectOtherProfile: already scheduled callback for " + device); 729 return; 730 } 731 mConnectOtherProfilesDeviceSet.add(device); 732 Message m = mHandler.obtainMessage(MESSAGE_CONNECT_OTHER_PROFILES); 733 m.obj = device; 734 mHandler.sendMessageDelayed(m, sConnectOtherProfilesTimeoutMillis); 735 } 736 737 // This function is called whenever a profile is connected. This allows any other bluetooth 738 // profiles which are not already connected or in the process of connecting to attempt to 739 // connect to the device that initiated the connection. In the event that this function is 740 // invoked and there are no current bluetooth connections no new profiles will be connected. 741 @RequiresPermission(allOf = { 742 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 743 android.Manifest.permission.MODIFY_PHONE_STATE, 744 }) processConnectOtherProfiles(BluetoothDevice device)745 private void processConnectOtherProfiles(BluetoothDevice device) { 746 debugLog("processConnectOtherProfiles, device=" + device); 747 if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) { 748 warnLog("processConnectOtherProfiles, adapter is not ON " + mAdapterService.getState()); 749 return; 750 } 751 if (handleAllProfilesDisconnected(device)) { 752 debugLog("processConnectOtherProfiles: all profiles disconnected for " + device); 753 return; 754 } 755 756 HeadsetService hsService = mFactory.getHeadsetService(); 757 A2dpService a2dpService = mFactory.getA2dpService(); 758 PanService panService = mFactory.getPanService(); 759 LeAudioService leAudioService = mFactory.getLeAudioService(); 760 CsipSetCoordinatorService csipSetCooridnatorService = 761 mFactory.getCsipSetCoordinatorService(); 762 VolumeControlService volumeControlService = 763 mFactory.getVolumeControlService(); 764 BatteryService batteryService = mFactory.getBatteryService(); 765 HidHostService hidHostService = mFactory.getHidHostService(); 766 BassClientService bcService = mFactory.getBassClientService(); 767 768 if (hsService != null) { 769 if (!mHeadsetRetrySet.contains(device) && (hsService.getConnectionPolicy(device) 770 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 771 && (hsService.getConnectionState(device) 772 == BluetoothProfile.STATE_DISCONNECTED)) { 773 debugLog("Retrying connection to Headset with device " + device); 774 mHeadsetRetrySet.add(device); 775 hsService.connect(device); 776 } 777 } 778 if (a2dpService != null) { 779 if (!mA2dpRetrySet.contains(device) && (a2dpService.getConnectionPolicy(device) 780 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 781 && (a2dpService.getConnectionState(device) 782 == BluetoothProfile.STATE_DISCONNECTED)) { 783 debugLog("Retrying connection to A2DP with device " + device); 784 mA2dpRetrySet.add(device); 785 a2dpService.connect(device); 786 } 787 } 788 if (panService != null) { 789 List<BluetoothDevice> panConnDevList = panService.getConnectedDevices(); 790 // TODO: the panConnDevList.isEmpty() check below should be removed once 791 // Multi-PAN is supported. 792 if (panConnDevList.isEmpty() && (panService.getConnectionPolicy(device) 793 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 794 && (panService.getConnectionState(device) 795 == BluetoothProfile.STATE_DISCONNECTED)) { 796 debugLog("Retrying connection to PAN with device " + device); 797 panService.connect(device); 798 } 799 } 800 if (leAudioService != null) { 801 List<BluetoothDevice> leAudioConnDevList = leAudioService.getConnectedDevices(); 802 if (!leAudioConnDevList.contains(device) && (leAudioService.getConnectionPolicy(device) 803 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 804 && (leAudioService.getConnectionState(device) 805 == BluetoothProfile.STATE_DISCONNECTED) 806 && mAdapterService.isLeAudioAllowed(device)) { 807 debugLog("Retrying connection to LEAudio with device " + device); 808 leAudioService.connect(device); 809 } 810 } 811 if (csipSetCooridnatorService != null) { 812 List<BluetoothDevice> csipConnDevList = csipSetCooridnatorService.getConnectedDevices(); 813 if (!csipConnDevList.contains(device) && (csipSetCooridnatorService.getConnectionPolicy(device) 814 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 815 && (csipSetCooridnatorService.getConnectionState(device) 816 == BluetoothProfile.STATE_DISCONNECTED)) { 817 debugLog("Retrying connection to CSIP with device " + device); 818 csipSetCooridnatorService.connect(device); 819 } 820 } 821 if (volumeControlService != null) { 822 List<BluetoothDevice> vcConnDevList = volumeControlService.getConnectedDevices(); 823 if (!vcConnDevList.contains(device) && (volumeControlService.getConnectionPolicy(device) 824 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 825 && (volumeControlService.getConnectionState(device) 826 == BluetoothProfile.STATE_DISCONNECTED)) { 827 debugLog("Retrying connection to VCP with device " + device); 828 volumeControlService.connect(device); 829 } 830 } 831 if (batteryService != null) { 832 List<BluetoothDevice> connectedDevices = batteryService.getConnectedDevices(); 833 if (!connectedDevices.contains(device) && (batteryService.getConnectionPolicy(device) 834 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 835 && (batteryService.getConnectionState(device) 836 == BluetoothProfile.STATE_DISCONNECTED)) { 837 debugLog("Retrying connection to BAS with device " + device); 838 batteryService.connect(device); 839 } 840 } 841 if (hidHostService != null) { 842 if ((hidHostService.getConnectionPolicy(device) 843 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 844 && (hidHostService.getConnectionState(device) 845 == BluetoothProfile.STATE_DISCONNECTED)) { 846 debugLog("Retrying connection to HID with device " + device); 847 hidHostService.connect(device); 848 } 849 } 850 if (bcService != null) { 851 List<BluetoothDevice> connectedDevices = bcService.getConnectedDevices(); 852 if (!connectedDevices.contains(device) && (bcService.getConnectionPolicy(device) 853 == BluetoothProfile.CONNECTION_POLICY_ALLOWED) 854 && (bcService.getConnectionState(device) 855 == BluetoothProfile.STATE_DISCONNECTED)) { 856 debugLog("Retrying connection to BASS with device " + device); 857 bcService.connect(device); 858 } 859 } 860 } 861 862 /** 863 * Direct call prior to sending out {@link BluetoothDevice#ACTION_UUID}. This indicates that 864 * service discovery is complete and passes the UUIDs directly to PhonePolicy. 865 * 866 * @param device is the remote device whose services have been discovered 867 * @param uuids are the services supported by the remote device 868 */ onUuidsDiscovered(BluetoothDevice device, ParcelUuid[] uuids)869 void onUuidsDiscovered(BluetoothDevice device, ParcelUuid[] uuids) { 870 debugLog("onUuidsDiscovered: discovered services for device " + device); 871 if (uuids != null) { 872 processInitProfilePriorities(device, uuids); 873 } else { 874 warnLog("onUuidsDiscovered: uuids is null for device " + device); 875 } 876 } 877 878 @VisibleForTesting setLeAudioEnabledByDefaultForTesting(boolean enabled)879 void setLeAudioEnabledByDefaultForTesting(boolean enabled) { 880 sLeAudioEnabledByDefault = enabled; 881 } 882 debugLog(String msg)883 private static void debugLog(String msg) { 884 if (DBG) { 885 Log.i(TAG, msg); 886 } 887 } 888 warnLog(String msg)889 private static void warnLog(String msg) { 890 Log.w(TAG, msg); 891 } 892 errorLog(String msg)893 private static void errorLog(String msg) { 894 Log.e(TAG, msg); 895 } 896 } 897