1 /* 2 * Copyright (C) 2012 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.a2dp; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 21 import static com.android.bluetooth.Utils.checkCallerTargetSdk; 22 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; 23 import static com.android.bluetooth.Utils.enforceCdmAssociation; 24 import static com.android.bluetooth.Utils.hasBluetoothPrivilegedPermission; 25 26 import android.annotation.NonNull; 27 import android.annotation.RequiresPermission; 28 import android.bluetooth.BluetoothA2dp; 29 import android.bluetooth.BluetoothA2dp.OptionalCodecsPreferenceStatus; 30 import android.bluetooth.BluetoothA2dp.OptionalCodecsSupportStatus; 31 import android.bluetooth.BluetoothAdapter; 32 import android.bluetooth.BluetoothCodecConfig; 33 import android.bluetooth.BluetoothCodecStatus; 34 import android.bluetooth.BluetoothDevice; 35 import android.bluetooth.BluetoothProfile; 36 import android.bluetooth.BluetoothUuid; 37 import android.bluetooth.BufferConstraints; 38 import android.bluetooth.IBluetoothA2dp; 39 import android.companion.CompanionDeviceManager; 40 import android.content.AttributionSource; 41 import android.content.BroadcastReceiver; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.IntentFilter; 45 import android.media.AudioManager; 46 import android.media.BluetoothProfileConnectionInfo; 47 import android.os.Binder; 48 import android.os.Build; 49 import android.os.Bundle; 50 import android.os.HandlerThread; 51 import android.sysprop.BluetoothProperties; 52 import android.util.Log; 53 54 import com.android.bluetooth.BluetoothMetricsProto; 55 import com.android.bluetooth.BluetoothStatsLog; 56 import com.android.bluetooth.Utils; 57 import com.android.bluetooth.btservice.AdapterService; 58 import com.android.bluetooth.btservice.MetricsLogger; 59 import com.android.bluetooth.btservice.ProfileService; 60 import com.android.bluetooth.btservice.ServiceFactory; 61 import com.android.bluetooth.btservice.storage.DatabaseManager; 62 import com.android.bluetooth.hfp.HeadsetService; 63 import com.android.internal.annotations.GuardedBy; 64 import com.android.internal.annotations.VisibleForTesting; 65 import com.android.modules.utils.SynchronousResultReceiver; 66 67 import java.util.ArrayList; 68 import java.util.List; 69 import java.util.Objects; 70 import java.util.concurrent.ConcurrentHashMap; 71 import java.util.concurrent.ConcurrentMap; 72 73 /** 74 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application. 75 * @hide 76 */ 77 public class A2dpService extends ProfileService { 78 private static final boolean DBG = true; 79 private static final String TAG = "A2dpService"; 80 81 // TODO(b/240635097): remove in U 82 private static final int SOURCE_CODEC_TYPE_OPUS = 6; 83 84 private static A2dpService sA2dpService; 85 86 private AdapterService mAdapterService; 87 private DatabaseManager mDatabaseManager; 88 private HandlerThread mStateMachinesThread; 89 90 @VisibleForTesting 91 A2dpNativeInterface mA2dpNativeInterface; 92 @VisibleForTesting 93 ServiceFactory mFactory = new ServiceFactory(); 94 @VisibleForTesting 95 AudioManager mAudioManager; 96 private A2dpCodecConfig mA2dpCodecConfig; 97 private CompanionDeviceManager mCompanionDeviceManager; 98 99 @GuardedBy("mStateMachines") 100 private BluetoothDevice mActiveDevice; 101 private final ConcurrentMap<BluetoothDevice, A2dpStateMachine> mStateMachines = 102 new ConcurrentHashMap<>(); 103 104 // Protect setActiveDevice()/removeActiveDevice() so all invoked is handled sequentially 105 private final Object mActiveSwitchingGuard = new Object(); 106 107 // Timeout for state machine thread join, to prevent potential ANR. 108 private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1000; 109 110 // Upper limit of all A2DP devices: Bonded or Connected 111 private static final int MAX_A2DP_STATE_MACHINES = 50; 112 // Upper limit of all A2DP devices that are Connected or Connecting 113 private int mMaxConnectedAudioDevices = 1; 114 // A2DP Offload Enabled in platform 115 boolean mA2dpOffloadEnabled = false; 116 117 private BroadcastReceiver mBondStateChangedReceiver; 118 isEnabled()119 public static boolean isEnabled() { 120 return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false); 121 } 122 123 @Override initBinder()124 protected IProfileServiceBinder initBinder() { 125 return new BluetoothA2dpBinder(this); 126 } 127 128 @Override create()129 protected void create() { 130 Log.i(TAG, "create()"); 131 } 132 133 @Override start()134 protected boolean start() { 135 Log.i(TAG, "start()"); 136 if (sA2dpService != null) { 137 throw new IllegalStateException("start() called twice"); 138 } 139 140 // Step 1: Get AdapterService, A2dpNativeInterface, DatabaseManager, AudioManager. 141 // None of them can be null. 142 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 143 "AdapterService cannot be null when A2dpService starts"); 144 mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(), 145 "A2dpNativeInterface cannot be null when A2dpService starts"); 146 mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), 147 "DatabaseManager cannot be null when A2dpService starts"); 148 mAudioManager = getSystemService(AudioManager.class); 149 mCompanionDeviceManager = getSystemService(CompanionDeviceManager.class); 150 Objects.requireNonNull(mAudioManager, 151 "AudioManager cannot be null when A2dpService starts"); 152 153 // Step 2: Get maximum number of connected audio devices 154 mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); 155 Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); 156 157 // Step 3: Start handler thread for state machines 158 mStateMachines.clear(); 159 mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); 160 mStateMachinesThread.start(); 161 162 // Step 4: Setup codec config 163 mA2dpCodecConfig = new A2dpCodecConfig(this, mA2dpNativeInterface); 164 165 // Step 5: Initialize native interface 166 mA2dpNativeInterface.init(mMaxConnectedAudioDevices, 167 mA2dpCodecConfig.codecConfigPriorities(), 168 mA2dpCodecConfig.codecConfigOffloading()); 169 170 // Step 6: Check if A2DP is in offload mode 171 mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled(); 172 if (DBG) { 173 Log.d(TAG, "A2DP offload flag set to " + mA2dpOffloadEnabled); 174 } 175 176 // Step 7: Setup broadcast receivers 177 IntentFilter filter = new IntentFilter(); 178 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 179 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 180 mBondStateChangedReceiver = new BondStateChangedReceiver(); 181 registerReceiver(mBondStateChangedReceiver, filter); 182 183 // Step 8: Mark service as started 184 setA2dpService(this); 185 BluetoothDevice activeDevice = getActiveDevice(); 186 String deviceAddress = activeDevice != null ? 187 activeDevice.getAddress() : 188 AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS; 189 mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress); 190 191 // Step 9: Clear active device 192 removeActiveDevice(false); 193 194 return true; 195 } 196 197 @Override stop()198 protected boolean stop() { 199 Log.i(TAG, "stop()"); 200 if (sA2dpService == null) { 201 Log.w(TAG, "stop() called before start()"); 202 return true; 203 } 204 205 // Step 9: Clear active device and stop playing audio 206 removeActiveDevice(true); 207 208 // Step 8: Mark service as stopped 209 BluetoothDevice activeDevice = getActiveDevice(); 210 String deviceAddress = activeDevice != null ? 211 activeDevice.getAddress() : 212 AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS; 213 mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress); 214 setA2dpService(null); 215 216 // Step 7: Unregister broadcast receivers 217 unregisterReceiver(mBondStateChangedReceiver); 218 mBondStateChangedReceiver = null; 219 220 // Step 6: Cleanup native interface 221 mA2dpNativeInterface.cleanup(); 222 mA2dpNativeInterface = null; 223 224 // Step 5: Clear codec config 225 mA2dpCodecConfig = null; 226 227 // Step 4: Destroy state machines and stop handler thread 228 synchronized (mStateMachines) { 229 for (A2dpStateMachine sm : mStateMachines.values()) { 230 sm.doQuit(); 231 sm.cleanup(); 232 } 233 mStateMachines.clear(); 234 } 235 236 if (mStateMachinesThread != null) { 237 try { 238 mStateMachinesThread.quitSafely(); 239 mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); 240 mStateMachinesThread = null; 241 } catch (InterruptedException e) { 242 // Do not rethrow as we are shutting down anyway 243 } 244 } 245 // Step 2: Reset maximum number of connected audio devices 246 mMaxConnectedAudioDevices = 1; 247 248 // Step 1: Clear AdapterService, A2dpNativeInterface, AudioManager 249 mAudioManager = null; 250 mA2dpNativeInterface = null; 251 mAdapterService = null; 252 253 return true; 254 } 255 256 @Override cleanup()257 protected void cleanup() { 258 Log.i(TAG, "cleanup()"); 259 } 260 getA2dpService()261 public static synchronized A2dpService getA2dpService() { 262 if (sA2dpService == null) { 263 Log.w(TAG, "getA2dpService(): service is null"); 264 return null; 265 } 266 if (!sA2dpService.isAvailable()) { 267 Log.w(TAG, "getA2dpService(): service is not available"); 268 return null; 269 } 270 return sA2dpService; 271 } 272 setA2dpService(A2dpService instance)273 private static synchronized void setA2dpService(A2dpService instance) { 274 if (DBG) { 275 Log.d(TAG, "setA2dpService(): set to: " + instance); 276 } 277 sA2dpService = instance; 278 } 279 connect(BluetoothDevice device)280 public boolean connect(BluetoothDevice device) { 281 if (DBG) { 282 Log.d(TAG, "connect(): " + device); 283 } 284 285 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 286 Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN"); 287 return false; 288 } 289 if (!Utils.arrayContains(mAdapterService.getRemoteUuids(device), 290 BluetoothUuid.A2DP_SINK)) { 291 Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID"); 292 return false; 293 } 294 295 synchronized (mStateMachines) { 296 if (!connectionAllowedCheckMaxDevices(device)) { 297 // when mMaxConnectedAudioDevices is one, disconnect current device first. 298 if (mMaxConnectedAudioDevices == 1) { 299 List<BluetoothDevice> sinks = getDevicesMatchingConnectionStates( 300 new int[] {BluetoothProfile.STATE_CONNECTED, 301 BluetoothProfile.STATE_CONNECTING, 302 BluetoothProfile.STATE_DISCONNECTING}); 303 for (BluetoothDevice sink : sinks) { 304 if (sink.equals(device)) { 305 Log.w(TAG, "Connecting to device " + device + " : disconnect skipped"); 306 continue; 307 } 308 disconnect(sink); 309 } 310 } else { 311 Log.e(TAG, "Cannot connect to " + device + " : too many connected devices"); 312 return false; 313 } 314 } 315 A2dpStateMachine smConnect = getOrCreateStateMachine(device); 316 if (smConnect == null) { 317 Log.e(TAG, "Cannot connect to " + device + " : no state machine"); 318 return false; 319 } 320 smConnect.sendMessage(A2dpStateMachine.CONNECT); 321 return true; 322 } 323 } 324 325 /** 326 * Disconnects A2dp for the remote bluetooth device 327 * 328 * @param device is the device with which we would like to disconnect a2dp 329 * @return true if profile disconnected, false if device not connected over a2dp 330 */ disconnect(BluetoothDevice device)331 public boolean disconnect(BluetoothDevice device) { 332 if (DBG) { 333 Log.d(TAG, "disconnect(): " + device); 334 } 335 336 synchronized (mStateMachines) { 337 A2dpStateMachine sm = mStateMachines.get(device); 338 if (sm == null) { 339 Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine"); 340 return false; 341 } 342 sm.sendMessage(A2dpStateMachine.DISCONNECT); 343 return true; 344 } 345 } 346 getConnectedDevices()347 public List<BluetoothDevice> getConnectedDevices() { 348 synchronized (mStateMachines) { 349 List<BluetoothDevice> devices = new ArrayList<>(); 350 for (A2dpStateMachine sm : mStateMachines.values()) { 351 if (sm.isConnected()) { 352 devices.add(sm.getDevice()); 353 } 354 } 355 return devices; 356 } 357 } 358 359 /** 360 * Check whether can connect to a peer device. 361 * The check considers the maximum number of connected peers. 362 * 363 * @param device the peer device to connect to 364 * @return true if connection is allowed, otherwise false 365 */ connectionAllowedCheckMaxDevices(BluetoothDevice device)366 private boolean connectionAllowedCheckMaxDevices(BluetoothDevice device) { 367 int connected = 0; 368 // Count devices that are in the process of connecting or already connected 369 synchronized (mStateMachines) { 370 for (A2dpStateMachine sm : mStateMachines.values()) { 371 switch (sm.getConnectionState()) { 372 case BluetoothProfile.STATE_CONNECTING: 373 case BluetoothProfile.STATE_CONNECTED: 374 if (Objects.equals(device, sm.getDevice())) { 375 return true; // Already connected or accounted for 376 } 377 connected++; 378 break; 379 default: 380 break; 381 } 382 } 383 } 384 return (connected < mMaxConnectedAudioDevices); 385 } 386 387 /** 388 * Check whether can connect to a peer device. 389 * The check considers a number of factors during the evaluation. 390 * 391 * @param device the peer device to connect to 392 * @param isOutgoingRequest if true, the check is for outgoing connection 393 * request, otherwise is for incoming connection request 394 * @return true if connection is allowed, otherwise false 395 */ 396 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) okToConnect(BluetoothDevice device, boolean isOutgoingRequest)397 public boolean okToConnect(BluetoothDevice device, boolean isOutgoingRequest) { 398 Log.i(TAG, "okToConnect: device " + device + " isOutgoingRequest: " + isOutgoingRequest); 399 // Check if this is an incoming connection in Quiet mode. 400 if (mAdapterService.isQuietModeEnabled() && !isOutgoingRequest) { 401 Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled"); 402 return false; 403 } 404 // Check if too many devices 405 if (!connectionAllowedCheckMaxDevices(device)) { 406 Log.e(TAG, "okToConnect: cannot connect to " + device 407 + " : too many connected devices"); 408 return false; 409 } 410 // Check connectionPolicy and accept or reject the connection. 411 int connectionPolicy = getConnectionPolicy(device); 412 int bondState = mAdapterService.getBondState(device); 413 // Allow this connection only if the device is bonded. Any attempt to connect while 414 // bonding would potentially lead to an unauthorized connection. 415 if (bondState != BluetoothDevice.BOND_BONDED) { 416 Log.w(TAG, "okToConnect: return false, bondState=" + bondState); 417 return false; 418 } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 419 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 420 if (!isOutgoingRequest) { 421 HeadsetService headsetService = HeadsetService.getHeadsetService(); 422 if (headsetService != null && headsetService.okToAcceptConnection(device, true)) { 423 Log.d(TAG, "okToConnect: return false," 424 + " Fallback connection to allowed HFP profile"); 425 headsetService.connect(device); 426 return false; 427 } 428 } 429 // Otherwise, reject the connection if connectionPolicy is not valid. 430 Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy); 431 return false; 432 } 433 return true; 434 } 435 getDevicesMatchingConnectionStates(int[] states)436 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 437 List<BluetoothDevice> devices = new ArrayList<>(); 438 if (states == null) { 439 return devices; 440 } 441 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 442 if (bondedDevices == null) { 443 return devices; 444 } 445 synchronized (mStateMachines) { 446 for (BluetoothDevice device : bondedDevices) { 447 if (!Utils.arrayContains(mAdapterService.getRemoteUuids(device), 448 BluetoothUuid.A2DP_SINK)) { 449 continue; 450 } 451 int connectionState = BluetoothProfile.STATE_DISCONNECTED; 452 A2dpStateMachine sm = mStateMachines.get(device); 453 if (sm != null) { 454 connectionState = sm.getConnectionState(); 455 } 456 for (int state : states) { 457 if (connectionState == state) { 458 devices.add(device); 459 break; 460 } 461 } 462 } 463 return devices; 464 } 465 } 466 467 /** 468 * Get the list of devices that have state machines. 469 * 470 * @return the list of devices that have state machines 471 */ 472 @VisibleForTesting getDevices()473 List<BluetoothDevice> getDevices() { 474 List<BluetoothDevice> devices = new ArrayList<>(); 475 synchronized (mStateMachines) { 476 for (A2dpStateMachine sm : mStateMachines.values()) { 477 devices.add(sm.getDevice()); 478 } 479 return devices; 480 } 481 } 482 getConnectionState(BluetoothDevice device)483 public int getConnectionState(BluetoothDevice device) { 484 synchronized (mStateMachines) { 485 A2dpStateMachine sm = mStateMachines.get(device); 486 if (sm == null) { 487 return BluetoothProfile.STATE_DISCONNECTED; 488 } 489 return sm.getConnectionState(); 490 } 491 } 492 493 /** 494 * Removes the current active device. 495 * 496 * @param stopAudio whether the current media playback should be stopped. 497 * @return true on success, otherwise false 498 */ removeActiveDevice(boolean stopAudio)499 public boolean removeActiveDevice(boolean stopAudio) { 500 synchronized (mActiveSwitchingGuard) { 501 BluetoothDevice previousActiveDevice = null; 502 synchronized (mStateMachines) { 503 if (mActiveDevice == null) return true; 504 previousActiveDevice = mActiveDevice; 505 } 506 507 // This needs to happen before we inform the audio manager that the device 508 // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. 509 updateAndBroadcastActiveDevice(null); 510 511 // Make sure the Audio Manager knows the previous active device is no longer active. 512 mAudioManager.handleBluetoothActiveDeviceChanged(null, previousActiveDevice, 513 BluetoothProfileConnectionInfo.createA2dpInfo(!stopAudio, -1)); 514 515 synchronized (mStateMachines) { 516 // Make sure the Active device in native layer is set to null and audio is off 517 if (!mA2dpNativeInterface.setActiveDevice(null)) { 518 Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native " 519 + "layer"); 520 return false; 521 } 522 } 523 } 524 return true; 525 } 526 527 /** 528 * Process a change in the silence mode for a {@link BluetoothDevice}. 529 * 530 * @param device the device to change silence mode 531 * @param silence true to enable silence mode, false to disable. 532 * @return true on success, false on error 533 */ 534 @VisibleForTesting setSilenceMode(@onNull BluetoothDevice device, boolean silence)535 public boolean setSilenceMode(@NonNull BluetoothDevice device, boolean silence) { 536 if (DBG) { 537 Log.d(TAG, "setSilenceMode(" + device + "): " + silence); 538 } 539 if (silence && Objects.equals(mActiveDevice, device)) { 540 removeActiveDevice(true); 541 } else if (!silence && mActiveDevice == null) { 542 // Set the device as the active device if currently no active device. 543 setActiveDevice(device); 544 } 545 if (!mA2dpNativeInterface.setSilenceDevice(device, silence)) { 546 Log.e(TAG, "Cannot set " + device + " silence mode " + silence + " in native layer"); 547 return false; 548 } 549 return true; 550 } 551 552 /** 553 * Set the active device. 554 * 555 * @param device the active device. Should not be null. 556 * @return true on success, otherwise false 557 */ setActiveDevice(@onNull BluetoothDevice device)558 public boolean setActiveDevice(@NonNull BluetoothDevice device) { 559 if (device == null) { 560 Log.e(TAG, "device should not be null!"); 561 return false; 562 } 563 564 synchronized (mActiveSwitchingGuard) { 565 A2dpStateMachine sm = null; 566 BluetoothDevice previousActiveDevice = null; 567 synchronized (mStateMachines) { 568 if (Objects.equals(device, mActiveDevice)) { 569 Log.i(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice 570 + " no changed"); 571 // returns true since the device is activated even double attempted 572 return true; 573 } 574 if (DBG) { 575 Log.d(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice); 576 } 577 sm = mStateMachines.get(device); 578 if (sm == null) { 579 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: " 580 + "no state machine"); 581 return false; 582 } 583 if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 584 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: " 585 + "device is not connected"); 586 return false; 587 } 588 previousActiveDevice = mActiveDevice; 589 } 590 591 // Switch from one A2DP to another A2DP device 592 if (DBG) { 593 Log.d(TAG, "Switch A2DP devices to " + device + " from " + previousActiveDevice); 594 } 595 // This needs to happen before we inform the audio manager that the device 596 // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. 597 updateAndBroadcastActiveDevice(device); 598 updateLowLatencyAudioSupport(device); 599 600 BluetoothDevice newActiveDevice = null; 601 synchronized (mStateMachines) { 602 if (!mA2dpNativeInterface.setActiveDevice(device)) { 603 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native " 604 + "layer"); 605 // Remove active device and stop playing audio. 606 removeActiveDevice(true); 607 return false; 608 } 609 // Send an intent with the active device codec config 610 BluetoothCodecStatus codecStatus = sm.getCodecStatus(); 611 if (codecStatus != null) { 612 broadcastCodecConfig(mActiveDevice, codecStatus); 613 } 614 newActiveDevice = mActiveDevice; 615 } 616 617 // Tasks of Bluetooth are done, and now restore the AudioManager side. 618 int rememberedVolume = -1; 619 if (mFactory.getAvrcpTargetService() != null) { 620 rememberedVolume = mFactory.getAvrcpTargetService() 621 .getRememberedVolumeForDevice(newActiveDevice); 622 } 623 // Make sure the Audio Manager knows the previous Active device is disconnected, 624 // and the new Active device is connected. 625 // And inform the Audio Service about the codec configuration 626 // change, so the Audio Service can reset accordingly the audio 627 // feeding parameters in the Audio HAL to the Bluetooth stack. 628 mAudioManager.handleBluetoothActiveDeviceChanged(newActiveDevice, previousActiveDevice, 629 BluetoothProfileConnectionInfo.createA2dpInfo(true, rememberedVolume)); 630 } 631 return true; 632 } 633 634 /** 635 * Get the active device. 636 * 637 * @return the active device or null if no device is active 638 */ getActiveDevice()639 public BluetoothDevice getActiveDevice() { 640 synchronized (mStateMachines) { 641 return mActiveDevice; 642 } 643 } 644 isActiveDevice(BluetoothDevice device)645 private boolean isActiveDevice(BluetoothDevice device) { 646 synchronized (mStateMachines) { 647 return (device != null) && Objects.equals(device, mActiveDevice); 648 } 649 } 650 651 /** 652 * Set connection policy of the profile and connects it if connectionPolicy is 653 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is 654 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 655 * 656 * <p> The device should already be paired. 657 * Connection policy can be one of: 658 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 659 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 660 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 661 * 662 * @param device Paired bluetooth device 663 * @param connectionPolicy is the connection policy to set to for this profile 664 * @return true if connectionPolicy is set, false on error 665 */ 666 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, int connectionPolicy)667 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 668 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 669 "Need BLUETOOTH_PRIVILEGED permission"); 670 if (DBG) { 671 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 672 } 673 674 if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.A2DP, 675 connectionPolicy)) { 676 return false; 677 } 678 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 679 connect(device); 680 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 681 disconnect(device); 682 } 683 return true; 684 } 685 686 /** 687 * Get the connection policy of the profile. 688 * 689 * <p> The connection policy can be any of: 690 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 691 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 692 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 693 * 694 * @param device Bluetooth device 695 * @return connection policy of the device 696 * @hide 697 */ getConnectionPolicy(BluetoothDevice device)698 public int getConnectionPolicy(BluetoothDevice device) { 699 return mDatabaseManager 700 .getProfileConnectionPolicy(device, BluetoothProfile.A2DP); 701 } 702 isAvrcpAbsoluteVolumeSupported()703 public boolean isAvrcpAbsoluteVolumeSupported() { 704 // TODO (apanicke): Add a hook here for the AvrcpTargetService. 705 return false; 706 } 707 708 setAvrcpAbsoluteVolume(int volume)709 public void setAvrcpAbsoluteVolume(int volume) { 710 // TODO (apanicke): Instead of using A2DP as a middleman for volume changes, add a binder 711 // service to the new AVRCP Profile and have the audio manager use that instead. 712 if (mFactory.getAvrcpTargetService() != null) { 713 mFactory.getAvrcpTargetService().sendVolumeChanged(volume); 714 return; 715 } 716 } 717 isA2dpPlaying(BluetoothDevice device)718 boolean isA2dpPlaying(BluetoothDevice device) { 719 if (DBG) { 720 Log.d(TAG, "isA2dpPlaying(" + device + ")"); 721 } 722 synchronized (mStateMachines) { 723 A2dpStateMachine sm = mStateMachines.get(device); 724 if (sm == null) { 725 return false; 726 } 727 return sm.isPlaying(); 728 } 729 } 730 731 /** 732 * Gets the current codec status (configuration and capability). 733 * 734 * @param device the remote Bluetooth device. If null, use the current 735 * active A2DP Bluetooth device. 736 * @return the current codec status 737 * @hide 738 */ getCodecStatus(BluetoothDevice device)739 public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { 740 if (DBG) { 741 Log.d(TAG, "getCodecStatus(" + device + ")"); 742 } 743 synchronized (mStateMachines) { 744 if (device == null) { 745 device = mActiveDevice; 746 } 747 if (device == null) { 748 return null; 749 } 750 A2dpStateMachine sm = mStateMachines.get(device); 751 if (sm != null) { 752 return sm.getCodecStatus(); 753 } 754 return null; 755 } 756 } 757 758 /** 759 * Sets the codec configuration preference. 760 * 761 * @param device the remote Bluetooth device. If null, use the currect 762 * active A2DP Bluetooth device. 763 * @param codecConfig the codec configuration preference 764 * @hide 765 */ setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig codecConfig)766 public void setCodecConfigPreference(BluetoothDevice device, 767 BluetoothCodecConfig codecConfig) { 768 if (DBG) { 769 Log.d(TAG, "setCodecConfigPreference(" + device + "): " 770 + Objects.toString(codecConfig)); 771 } 772 if (device == null) { 773 device = mActiveDevice; 774 } 775 if (device == null) { 776 Log.e(TAG, "setCodecConfigPreference: Invalid device"); 777 return; 778 } 779 if (codecConfig == null) { 780 Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); 781 return; 782 } 783 BluetoothCodecStatus codecStatus = getCodecStatus(device); 784 if (codecStatus == null) { 785 Log.e(TAG, "setCodecConfigPreference: Codec status is null"); 786 return; 787 } 788 mA2dpCodecConfig.setCodecConfigPreference(device, codecStatus, codecConfig); 789 } 790 791 /** 792 * Enables the optional codecs. 793 * 794 * @param device the remote Bluetooth device. If null, use the currect 795 * active A2DP Bluetooth device. 796 * @hide 797 */ enableOptionalCodecs(BluetoothDevice device)798 public void enableOptionalCodecs(BluetoothDevice device) { 799 if (DBG) { 800 Log.d(TAG, "enableOptionalCodecs(" + device + ")"); 801 } 802 if (device == null) { 803 device = mActiveDevice; 804 } 805 if (device == null) { 806 Log.e(TAG, "enableOptionalCodecs: Invalid device"); 807 return; 808 } 809 if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) { 810 Log.e(TAG, "enableOptionalCodecs: No optional codecs"); 811 return; 812 } 813 BluetoothCodecStatus codecStatus = getCodecStatus(device); 814 if (codecStatus == null) { 815 Log.e(TAG, "enableOptionalCodecs: Codec status is null"); 816 return; 817 } 818 updateLowLatencyAudioSupport(device); 819 mA2dpCodecConfig.enableOptionalCodecs(device, codecStatus.getCodecConfig()); 820 } 821 822 /** 823 * Disables the optional codecs. 824 * 825 * @param device the remote Bluetooth device. If null, use the currect 826 * active A2DP Bluetooth device. 827 * @hide 828 */ disableOptionalCodecs(BluetoothDevice device)829 public void disableOptionalCodecs(BluetoothDevice device) { 830 if (DBG) { 831 Log.d(TAG, "disableOptionalCodecs(" + device + ")"); 832 } 833 if (device == null) { 834 device = mActiveDevice; 835 } 836 if (device == null) { 837 Log.e(TAG, "disableOptionalCodecs: Invalid device"); 838 return; 839 } 840 if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) { 841 Log.e(TAG, "disableOptionalCodecs: No optional codecs"); 842 return; 843 } 844 BluetoothCodecStatus codecStatus = getCodecStatus(device); 845 if (codecStatus == null) { 846 Log.e(TAG, "disableOptionalCodecs: Codec status is null"); 847 return; 848 } 849 updateLowLatencyAudioSupport(device); 850 mA2dpCodecConfig.disableOptionalCodecs(device, codecStatus.getCodecConfig()); 851 } 852 853 /** 854 * Checks whether optional codecs are supported 855 * 856 * @param device is the remote bluetooth device. 857 * @return whether optional codecs are supported. Possible values are: 858 * {@link OptionalCodecsSupportStatus#OPTIONAL_CODECS_SUPPORTED}, 859 * {@link OptionalCodecsSupportStatus#OPTIONAL_CODECS_NOT_SUPPORTED}, 860 * {@link OptionalCodecsSupportStatus#OPTIONAL_CODECS_SUPPORT_UNKNOWN}. 861 */ getSupportsOptionalCodecs(BluetoothDevice device)862 public @OptionalCodecsSupportStatus int getSupportsOptionalCodecs(BluetoothDevice device) { 863 return mDatabaseManager.getA2dpSupportsOptionalCodecs(device); 864 } 865 setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport)866 public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) { 867 int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED 868 : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED; 869 mDatabaseManager.setA2dpSupportsOptionalCodecs(device, value); 870 } 871 872 /** 873 * Checks whether optional codecs are enabled 874 * 875 * @param device is the remote bluetooth device 876 * @return whether the optional codecs are enabled. Possible values are: 877 * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_ENABLED}, 878 * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_DISABLED}, 879 * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_UNKNOWN}. 880 */ getOptionalCodecsEnabled(BluetoothDevice device)881 public @OptionalCodecsPreferenceStatus int getOptionalCodecsEnabled(BluetoothDevice device) { 882 return mDatabaseManager.getA2dpOptionalCodecsEnabled(device); 883 } 884 885 /** 886 * Sets the optional codecs to be set to the passed in value 887 * 888 * @param device is the remote bluetooth device 889 * @param value is the new status for the optional codecs. Possible values are: 890 * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_ENABLED}, 891 * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_DISABLED}, 892 * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_UNKNOWN}. 893 */ setOptionalCodecsEnabled(BluetoothDevice device, @OptionalCodecsPreferenceStatus int value)894 public void setOptionalCodecsEnabled(BluetoothDevice device, 895 @OptionalCodecsPreferenceStatus int value) { 896 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN 897 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED 898 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 899 Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value); 900 return; 901 } 902 mDatabaseManager.setA2dpOptionalCodecsEnabled(device, value); 903 } 904 905 /** 906 * Get dynamic audio buffer size supported type 907 * 908 * @return support <p>Possible values are 909 * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_NONE}, 910 * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD}, 911 * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}. 912 */ 913 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getDynamicBufferSupport()914 public int getDynamicBufferSupport() { 915 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 916 "Need BLUETOOTH_PRIVILEGED permission"); 917 return mAdapterService.getDynamicBufferSupport(); 918 } 919 920 /** 921 * Get dynamic audio buffer size 922 * 923 * @return BufferConstraints 924 */ 925 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getBufferConstraints()926 public BufferConstraints getBufferConstraints() { 927 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 928 "Need BLUETOOTH_PRIVILEGED permission"); 929 return mAdapterService.getBufferConstraints(); 930 } 931 932 /** 933 * Set dynamic audio buffer size 934 * 935 * @param codec Audio codec 936 * @param value buffer millis 937 * @return true if the settings is successful, false otherwise 938 */ 939 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setBufferLengthMillis(int codec, int value)940 public boolean setBufferLengthMillis(int codec, int value) { 941 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 942 "Need BLUETOOTH_PRIVILEGED permission"); 943 return mAdapterService.setBufferLengthMillis(codec, value); 944 } 945 946 // Handle messages from native (JNI) to Java messageFromNative(A2dpStackEvent stackEvent)947 void messageFromNative(A2dpStackEvent stackEvent) { 948 Objects.requireNonNull(stackEvent.device, 949 "Device should never be null, event: " + stackEvent); 950 synchronized (mStateMachines) { 951 BluetoothDevice device = stackEvent.device; 952 A2dpStateMachine sm = mStateMachines.get(device); 953 if (sm == null) { 954 if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 955 switch (stackEvent.valueInt) { 956 case A2dpStackEvent.CONNECTION_STATE_CONNECTED: 957 case A2dpStackEvent.CONNECTION_STATE_CONNECTING: 958 // Create a new state machine only when connecting to a device 959 if (!connectionAllowedCheckMaxDevices(device)) { 960 Log.e(TAG, "Cannot connect to " + device 961 + " : too many connected devices"); 962 return; 963 } 964 sm = getOrCreateStateMachine(device); 965 break; 966 default: 967 break; 968 } 969 } 970 } 971 if (sm == null) { 972 Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); 973 return; 974 } 975 sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); 976 } 977 } 978 979 /** 980 * The codec configuration for a device has been updated. 981 * 982 * @param device the remote device 983 * @param codecStatus the new codec status 984 * @param sameAudioFeedingParameters if true the audio feeding parameters 985 * haven't been changed 986 */ 987 @VisibleForTesting codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, boolean sameAudioFeedingParameters)988 public void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, 989 boolean sameAudioFeedingParameters) { 990 // Log codec config and capability metrics 991 BluetoothCodecConfig codecConfig = codecStatus.getCodecConfig(); 992 int metricId = mAdapterService.getMetricId(device); 993 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_A2DP_CODEC_CONFIG_CHANGED, 994 mAdapterService.obfuscateAddress(device), codecConfig.getCodecType(), 995 codecConfig.getCodecPriority(), codecConfig.getSampleRate(), 996 codecConfig.getBitsPerSample(), codecConfig.getChannelMode(), 997 codecConfig.getCodecSpecific1(), codecConfig.getCodecSpecific2(), 998 codecConfig.getCodecSpecific3(), codecConfig.getCodecSpecific4(), metricId); 999 List<BluetoothCodecConfig> codecCapabilities = 1000 codecStatus.getCodecsSelectableCapabilities(); 1001 for (BluetoothCodecConfig codecCapability : codecCapabilities) { 1002 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_A2DP_CODEC_CAPABILITY_CHANGED, 1003 mAdapterService.obfuscateAddress(device), codecCapability.getCodecType(), 1004 codecCapability.getCodecPriority(), codecCapability.getSampleRate(), 1005 codecCapability.getBitsPerSample(), codecCapability.getChannelMode(), 1006 codecConfig.getCodecSpecific1(), codecConfig.getCodecSpecific2(), 1007 codecConfig.getCodecSpecific3(), codecConfig.getCodecSpecific4(), metricId); 1008 } 1009 1010 broadcastCodecConfig(device, codecStatus); 1011 1012 // Inform the Audio Service about the codec configuration change, 1013 // so the Audio Service can reset accordingly the audio feeding 1014 // parameters in the Audio HAL to the Bluetooth stack. 1015 // Until we are able to detect from device_port_proxy if the config has changed or not, 1016 // the Bluetooth stack can only disable the audio session and need to ask audioManager to 1017 // restart the session even if feeding parameter are the same. (sameAudioFeedingParameters 1018 // is left unused until there) 1019 if (isActiveDevice(device)) { 1020 mAudioManager.handleBluetoothActiveDeviceChanged(device, device, 1021 BluetoothProfileConnectionInfo.createA2dpInfo(false, -1)); 1022 } 1023 } 1024 getOrCreateStateMachine(BluetoothDevice device)1025 private A2dpStateMachine getOrCreateStateMachine(BluetoothDevice device) { 1026 if (device == null) { 1027 Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null"); 1028 return null; 1029 } 1030 synchronized (mStateMachines) { 1031 A2dpStateMachine sm = mStateMachines.get(device); 1032 if (sm != null) { 1033 return sm; 1034 } 1035 // Limit the maximum number of state machines to avoid DoS attack 1036 if (mStateMachines.size() >= MAX_A2DP_STATE_MACHINES) { 1037 Log.e(TAG, "Maximum number of A2DP state machines reached: " 1038 + MAX_A2DP_STATE_MACHINES); 1039 return null; 1040 } 1041 if (DBG) { 1042 Log.d(TAG, "Creating a new state machine for " + device); 1043 } 1044 sm = A2dpStateMachine.make(device, this, mA2dpNativeInterface, 1045 mStateMachinesThread.getLooper()); 1046 mStateMachines.put(device, sm); 1047 return sm; 1048 } 1049 } 1050 1051 // This needs to run before any of the Audio Manager connection functions since 1052 // AVRCP needs to be aware that the audio device is changed before the Audio Manager 1053 // changes the volume of the output devices. updateAndBroadcastActiveDevice(BluetoothDevice device)1054 private void updateAndBroadcastActiveDevice(BluetoothDevice device) { 1055 if (DBG) { 1056 Log.d(TAG, "updateAndBroadcastActiveDevice(" + device + ")"); 1057 } 1058 1059 // Make sure volume has been store before device been remove from active. 1060 if (mFactory.getAvrcpTargetService() != null) { 1061 mFactory.getAvrcpTargetService().volumeDeviceSwitched(device); 1062 } 1063 synchronized (mStateMachines) { 1064 mActiveDevice = device; 1065 } 1066 1067 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED, 1068 BluetoothProfile.A2DP, mAdapterService.obfuscateAddress(device), 1069 mAdapterService.getMetricId(device)); 1070 Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); 1071 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1072 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1073 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1074 Utils.sendBroadcast(this, intent, BLUETOOTH_CONNECT, 1075 Utils.getTempAllowlistBroadcastOptions()); 1076 } 1077 broadcastCodecConfig(BluetoothDevice device, BluetoothCodecStatus codecStatus)1078 private void broadcastCodecConfig(BluetoothDevice device, BluetoothCodecStatus codecStatus) { 1079 if (DBG) { 1080 Log.d(TAG, "broadcastCodecConfig(" + device + "): " + codecStatus); 1081 } 1082 Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED); 1083 intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, codecStatus); 1084 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1085 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1086 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1087 Utils.sendBroadcast(this, intent, BLUETOOTH_CONNECT, 1088 Utils.getTempAllowlistBroadcastOptions()); 1089 } 1090 1091 private class BondStateChangedReceiver extends BroadcastReceiver { 1092 @Override onReceive(Context context, Intent intent)1093 public void onReceive(Context context, Intent intent) { 1094 if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { 1095 return; 1096 } 1097 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 1098 BluetoothDevice.ERROR); 1099 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 1100 Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 1101 bondStateChanged(device, state); 1102 } 1103 } 1104 1105 /** 1106 * Process a change in the bonding state for a device. 1107 * 1108 * @param device the device whose bonding state has changed 1109 * @param bondState the new bond state for the device. Possible values are: 1110 * {@link BluetoothDevice#BOND_NONE}, 1111 * {@link BluetoothDevice#BOND_BONDING}, 1112 * {@link BluetoothDevice#BOND_BONDED}. 1113 */ 1114 @VisibleForTesting bondStateChanged(BluetoothDevice device, int bondState)1115 void bondStateChanged(BluetoothDevice device, int bondState) { 1116 if (DBG) { 1117 Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState); 1118 } 1119 // Remove state machine if the bonding for a device is removed 1120 if (bondState != BluetoothDevice.BOND_NONE) { 1121 return; 1122 } 1123 synchronized (mStateMachines) { 1124 A2dpStateMachine sm = mStateMachines.get(device); 1125 if (sm == null) { 1126 return; 1127 } 1128 if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 1129 return; 1130 } 1131 } 1132 if (mFactory.getAvrcpTargetService() != null) { 1133 mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device); 1134 } 1135 removeStateMachine(device); 1136 } 1137 removeStateMachine(BluetoothDevice device)1138 private void removeStateMachine(BluetoothDevice device) { 1139 synchronized (mStateMachines) { 1140 A2dpStateMachine sm = mStateMachines.get(device); 1141 if (sm == null) { 1142 Log.w(TAG, "removeStateMachine: device " + device 1143 + " does not have a state machine"); 1144 return; 1145 } 1146 Log.i(TAG, "removeStateMachine: removing state machine for device: " + device); 1147 sm.doQuit(); 1148 sm.cleanup(); 1149 mStateMachines.remove(device); 1150 } 1151 } 1152 1153 1154 /** 1155 * Update and initiate optional codec status change to native. 1156 * 1157 * @param device the device to change optional codec status 1158 */ 1159 @VisibleForTesting updateOptionalCodecsSupport(BluetoothDevice device)1160 public void updateOptionalCodecsSupport(BluetoothDevice device) { 1161 int previousSupport = getSupportsOptionalCodecs(device); 1162 boolean supportsOptional = false; 1163 boolean hasMandatoryCodec = false; 1164 1165 synchronized (mStateMachines) { 1166 A2dpStateMachine sm = mStateMachines.get(device); 1167 if (sm == null) { 1168 return; 1169 } 1170 BluetoothCodecStatus codecStatus = sm.getCodecStatus(); 1171 if (codecStatus != null) { 1172 for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) { 1173 if (config.isMandatoryCodec()) { 1174 hasMandatoryCodec = true; 1175 } else { 1176 supportsOptional = true; 1177 } 1178 } 1179 } 1180 } 1181 if (!hasMandatoryCodec) { 1182 // Mandatory codec(SBC) is not selectable. It could be caused by the remote device 1183 // select codec before native finish get codec capabilities. Stop use this codec 1184 // status as the reference to support/enable optional codecs. 1185 Log.i(TAG, "updateOptionalCodecsSupport: Mandatory codec is not selectable."); 1186 return; 1187 } 1188 1189 if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN 1190 || supportsOptional != (previousSupport 1191 == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { 1192 setSupportsOptionalCodecs(device, supportsOptional); 1193 } 1194 if (supportsOptional) { 1195 int enabled = getOptionalCodecsEnabled(device); 1196 switch (enabled) { 1197 case BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN: 1198 // Enable optional codec by default. 1199 setOptionalCodecsEnabled(device, BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); 1200 // Fall through intended 1201 case BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED: 1202 enableOptionalCodecs(device); 1203 break; 1204 case BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED: 1205 disableOptionalCodecs(device); 1206 break; 1207 } 1208 } 1209 } 1210 1211 /** 1212 * Check for low-latency codec support and inform AdapterService 1213 * 1214 * @param device device whose audio low latency will be allowed or disallowed 1215 */ 1216 @VisibleForTesting updateLowLatencyAudioSupport(BluetoothDevice device)1217 public void updateLowLatencyAudioSupport(BluetoothDevice device) { 1218 synchronized (mStateMachines) { 1219 A2dpStateMachine sm = mStateMachines.get(device); 1220 if (sm == null) { 1221 return; 1222 } 1223 BluetoothCodecStatus codecStatus = sm.getCodecStatus(); 1224 boolean lowLatencyAudioAllow = false; 1225 BluetoothCodecConfig lowLatencyCodec = new BluetoothCodecConfig.Builder() 1226 .setCodecType(SOURCE_CODEC_TYPE_OPUS) // remove in U 1227 .build(); 1228 1229 if (codecStatus != null 1230 && codecStatus.isCodecConfigSelectable(lowLatencyCodec) 1231 && getOptionalCodecsEnabled(device) 1232 == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 1233 lowLatencyAudioAllow = true; 1234 } 1235 mAdapterService.allowLowLatencyAudio(lowLatencyAudioAllow, device); 1236 } 1237 } 1238 connectionStateChanged(BluetoothDevice device, int fromState, int toState)1239 void connectionStateChanged(BluetoothDevice device, int fromState, int toState) { 1240 if ((device == null) || (fromState == toState)) { 1241 return; 1242 } 1243 if (toState == BluetoothProfile.STATE_CONNECTED) { 1244 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); 1245 } 1246 // Set the active device if only one connected device is supported and it was connected 1247 if (toState == BluetoothProfile.STATE_CONNECTED && (mMaxConnectedAudioDevices == 1)) { 1248 setActiveDevice(device); 1249 } 1250 // When disconnected, ActiveDeviceManager will call setActiveDevice(null) 1251 1252 // Check if the device is disconnected - if unbond, remove the state machine 1253 if (toState == BluetoothProfile.STATE_DISCONNECTED) { 1254 if (mAdapterService.getBondState(device) == BluetoothDevice.BOND_NONE) { 1255 if (mFactory.getAvrcpTargetService() != null) { 1256 mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device); 1257 } 1258 removeStateMachine(device); 1259 } 1260 } 1261 } 1262 1263 /** 1264 * Retrieves the most recently connected device in the A2DP connected devices list. 1265 */ getFallbackDevice()1266 public BluetoothDevice getFallbackDevice() { 1267 DatabaseManager dbManager = mAdapterService.getDatabase(); 1268 return dbManager != null ? dbManager 1269 .getMostRecentlyConnectedDevicesInList(getConnectedDevices()) 1270 : null; 1271 } 1272 1273 /** 1274 * Binder object: must be a static class or memory leak may occur. 1275 */ 1276 @VisibleForTesting 1277 static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 1278 implements IProfileServiceBinder { 1279 private A2dpService mService; 1280 1281 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)1282 private A2dpService getService(AttributionSource source) { 1283 if (Utils.isInstrumentationTestMode()) { 1284 return mService; 1285 } 1286 if (!Utils.checkServiceAvailable(mService, TAG) 1287 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 1288 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 1289 return null; 1290 } 1291 return mService; 1292 } 1293 BluetoothA2dpBinder(A2dpService svc)1294 BluetoothA2dpBinder(A2dpService svc) { 1295 mService = svc; 1296 } 1297 1298 @Override cleanup()1299 public void cleanup() { 1300 mService = null; 1301 } 1302 1303 @Override connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1304 public void connect(BluetoothDevice device, AttributionSource source, 1305 SynchronousResultReceiver receiver) { 1306 try { 1307 A2dpService service = getService(source); 1308 boolean result = false; 1309 if (service != null) { 1310 result = service.connect(device); 1311 } 1312 receiver.send(result); 1313 } catch (RuntimeException e) { 1314 receiver.propagateException(e); 1315 } 1316 } 1317 1318 @Override disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1319 public void disconnect(BluetoothDevice device, AttributionSource source, 1320 SynchronousResultReceiver receiver) { 1321 try { 1322 A2dpService service = getService(source); 1323 boolean result = false; 1324 if (service != null) { 1325 result = service.disconnect(device); 1326 } 1327 receiver.send(result); 1328 } catch (RuntimeException e) { 1329 receiver.propagateException(e); 1330 } 1331 } 1332 1333 @Override getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)1334 public void getConnectedDevices(AttributionSource source, 1335 SynchronousResultReceiver receiver) { 1336 try { 1337 A2dpService service = getService(source); 1338 List<BluetoothDevice> connectedDevices = new ArrayList<>(0); 1339 if (service != null) { 1340 connectedDevices = service.getConnectedDevices(); 1341 } 1342 receiver.send(connectedDevices); 1343 } catch (RuntimeException e) { 1344 receiver.propagateException(e); 1345 } 1346 } 1347 1348 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)1349 public void getDevicesMatchingConnectionStates(int[] states, 1350 AttributionSource source, SynchronousResultReceiver receiver) { 1351 try { 1352 A2dpService service = getService(source); 1353 List<BluetoothDevice> connectedDevices = new ArrayList<>(0); 1354 if (service != null) { 1355 connectedDevices = service.getDevicesMatchingConnectionStates(states); 1356 } 1357 receiver.send(connectedDevices); 1358 } catch (RuntimeException e) { 1359 receiver.propagateException(e); 1360 } 1361 } 1362 1363 @Override getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1364 public void getConnectionState(BluetoothDevice device, 1365 AttributionSource source, SynchronousResultReceiver receiver) { 1366 try { 1367 A2dpService service = getService(source); 1368 int state = BluetoothProfile.STATE_DISCONNECTED; 1369 if (service != null) { 1370 state = service.getConnectionState(device); 1371 } 1372 receiver.send(state); 1373 } catch (RuntimeException e) { 1374 receiver.propagateException(e); 1375 } 1376 } 1377 1378 @Override setActiveDevice(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1379 public void setActiveDevice(BluetoothDevice device, AttributionSource source, 1380 SynchronousResultReceiver receiver) { 1381 try { 1382 A2dpService service = getService(source); 1383 boolean result = false; 1384 if (service != null) { 1385 if (device == null) { 1386 result = service.removeActiveDevice(false); 1387 } else { 1388 result = service.setActiveDevice(device); 1389 } 1390 } 1391 receiver.send(result); 1392 } catch (RuntimeException e) { 1393 receiver.propagateException(e); 1394 } 1395 } 1396 1397 @Override getActiveDevice(AttributionSource source, SynchronousResultReceiver receiver)1398 public void getActiveDevice(AttributionSource source, SynchronousResultReceiver receiver) { 1399 try { 1400 A2dpService service = getService(source); 1401 BluetoothDevice activeDevice = null; 1402 if (service != null) { 1403 activeDevice = service.getActiveDevice(); 1404 } 1405 receiver.send(activeDevice); 1406 } catch (RuntimeException e) { 1407 receiver.propagateException(e); 1408 } 1409 } 1410 1411 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver)1412 public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, 1413 AttributionSource source, SynchronousResultReceiver receiver) { 1414 try { 1415 A2dpService service = getService(source); 1416 boolean result = false; 1417 if (service != null) { 1418 result = service.setConnectionPolicy(device, connectionPolicy); 1419 } 1420 receiver.send(result); 1421 } catch (RuntimeException e) { 1422 receiver.propagateException(e); 1423 } 1424 } 1425 1426 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1427 public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, 1428 SynchronousResultReceiver receiver) { 1429 try { 1430 A2dpService service = getService(source); 1431 int result = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 1432 if (service != null) { 1433 enforceBluetoothPrivilegedPermission(service); 1434 result = service.getConnectionPolicy(device); 1435 } 1436 receiver.send(result); 1437 } catch (RuntimeException e) { 1438 receiver.propagateException(e); 1439 } 1440 } 1441 1442 @Override isAvrcpAbsoluteVolumeSupported(SynchronousResultReceiver receiver)1443 public void isAvrcpAbsoluteVolumeSupported(SynchronousResultReceiver receiver) { 1444 // TODO (apanicke): Add a hook here for the AvrcpTargetService. 1445 receiver.send(false); 1446 } 1447 1448 @Override setAvrcpAbsoluteVolume(int volume, AttributionSource source)1449 public void setAvrcpAbsoluteVolume(int volume, AttributionSource source) { 1450 A2dpService service = getService(source); 1451 if (service == null) { 1452 return; 1453 } 1454 enforceBluetoothPrivilegedPermission(service); 1455 service.setAvrcpAbsoluteVolume(volume); 1456 } 1457 1458 @Override isA2dpPlaying(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1459 public void isA2dpPlaying(BluetoothDevice device, AttributionSource source, 1460 SynchronousResultReceiver receiver) { 1461 try { 1462 A2dpService service = getService(source); 1463 boolean result = false; 1464 if (service != null) { 1465 result = service.isA2dpPlaying(device); 1466 } 1467 receiver.send(result); 1468 } catch (RuntimeException e) { 1469 receiver.propagateException(e); 1470 } 1471 } 1472 1473 @Override getCodecStatus(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1474 public void getCodecStatus(BluetoothDevice device, 1475 AttributionSource source, SynchronousResultReceiver receiver) { 1476 try { 1477 A2dpService service = getService(source); 1478 BluetoothCodecStatus codecStatus = null; 1479 if (service != null) { 1480 codecStatus = service.getCodecStatus(device); 1481 } 1482 receiver.send(codecStatus); 1483 } catch (RuntimeException e) { 1484 receiver.propagateException(e); 1485 } 1486 } 1487 1488 @Override setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig codecConfig, AttributionSource source)1489 public void setCodecConfigPreference(BluetoothDevice device, 1490 BluetoothCodecConfig codecConfig, AttributionSource source) { 1491 A2dpService service = getService(source); 1492 if (service == null) { 1493 return; 1494 } 1495 if (!hasBluetoothPrivilegedPermission(service)) { 1496 enforceCdmAssociation(service.mCompanionDeviceManager, service, 1497 source.getPackageName(), Binder.getCallingUid(), device); 1498 } 1499 service.setCodecConfigPreference(device, codecConfig); 1500 } 1501 1502 @Override enableOptionalCodecs(BluetoothDevice device, AttributionSource source)1503 public void enableOptionalCodecs(BluetoothDevice device, AttributionSource source) { 1504 A2dpService service = getService(source); 1505 if (service == null) { 1506 return; 1507 } 1508 if (checkCallerTargetSdk(mService, source.getPackageName(), 1509 Build.VERSION_CODES.TIRAMISU)) { 1510 enforceBluetoothPrivilegedPermission(service); 1511 } 1512 service.enableOptionalCodecs(device); 1513 } 1514 1515 @Override disableOptionalCodecs(BluetoothDevice device, AttributionSource source)1516 public void disableOptionalCodecs(BluetoothDevice device, AttributionSource source) { 1517 A2dpService service = getService(source); 1518 if (service == null) { 1519 return; 1520 } 1521 if (checkCallerTargetSdk(mService, source.getPackageName(), 1522 Build.VERSION_CODES.TIRAMISU)) { 1523 enforceBluetoothPrivilegedPermission(service); 1524 } 1525 service.disableOptionalCodecs(device); 1526 } 1527 1528 @Override isOptionalCodecsSupported(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1529 public void isOptionalCodecsSupported(BluetoothDevice device, AttributionSource source, 1530 SynchronousResultReceiver receiver) { 1531 try { 1532 A2dpService service = getService(source); 1533 int codecSupport = BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; 1534 if (service != null) { 1535 if (checkCallerTargetSdk(mService, source.getPackageName(), 1536 Build.VERSION_CODES.TIRAMISU)) { 1537 enforceBluetoothPrivilegedPermission(service); 1538 } 1539 codecSupport = service.getSupportsOptionalCodecs(device); 1540 } 1541 receiver.send(codecSupport); 1542 } catch (RuntimeException e) { 1543 receiver.propagateException(e); 1544 } 1545 } 1546 1547 @Override isOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)1548 public void isOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source, 1549 SynchronousResultReceiver receiver) { 1550 try { 1551 A2dpService service = getService(source); 1552 int optionalCodecEnabled = BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; 1553 if (service != null) { 1554 if (checkCallerTargetSdk(mService, source.getPackageName(), 1555 Build.VERSION_CODES.TIRAMISU)) { 1556 enforceBluetoothPrivilegedPermission(service); 1557 } 1558 optionalCodecEnabled = service.getOptionalCodecsEnabled(device); 1559 } 1560 receiver.send(optionalCodecEnabled); 1561 } catch (RuntimeException e) { 1562 receiver.propagateException(e); 1563 } 1564 } 1565 1566 @Override setOptionalCodecsEnabled(BluetoothDevice device, int value, AttributionSource source)1567 public void setOptionalCodecsEnabled(BluetoothDevice device, int value, 1568 AttributionSource source) { 1569 A2dpService service = getService(source); 1570 if (service == null) { 1571 return; 1572 } 1573 if (checkCallerTargetSdk(mService, source.getPackageName(), 1574 Build.VERSION_CODES.TIRAMISU)) { 1575 enforceBluetoothPrivilegedPermission(service); 1576 } 1577 service.setOptionalCodecsEnabled(device, value); 1578 } 1579 1580 @Override getDynamicBufferSupport(AttributionSource source, SynchronousResultReceiver receiver)1581 public void getDynamicBufferSupport(AttributionSource source, 1582 SynchronousResultReceiver receiver) { 1583 try { 1584 A2dpService service = getService(source); 1585 int bufferSupport = BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE; 1586 if (service != null) { 1587 bufferSupport = service.getDynamicBufferSupport(); 1588 } 1589 receiver.send(bufferSupport); 1590 } catch (RuntimeException e) { 1591 receiver.propagateException(e); 1592 } 1593 } 1594 1595 @Override getBufferConstraints(AttributionSource source, SynchronousResultReceiver receiver)1596 public void getBufferConstraints(AttributionSource source, 1597 SynchronousResultReceiver receiver) { 1598 try { 1599 A2dpService service = getService(source); 1600 BufferConstraints bufferConstraints = null; 1601 if (service != null) { 1602 bufferConstraints = service.getBufferConstraints(); 1603 } 1604 receiver.send(bufferConstraints); 1605 } catch (RuntimeException e) { 1606 receiver.propagateException(e); 1607 } 1608 } 1609 1610 @Override setBufferLengthMillis(int codec, int value, AttributionSource source, SynchronousResultReceiver receiver)1611 public void setBufferLengthMillis(int codec, int value, AttributionSource source, 1612 SynchronousResultReceiver receiver) { 1613 try { 1614 A2dpService service = getService(source); 1615 boolean result = false; 1616 if (service != null) { 1617 result = service.setBufferLengthMillis(codec, value); 1618 } 1619 receiver.send(result); 1620 } catch (RuntimeException e) { 1621 receiver.propagateException(e); 1622 } 1623 } 1624 } 1625 1626 @Override dump(StringBuilder sb)1627 public void dump(StringBuilder sb) { 1628 super.dump(sb); 1629 ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); 1630 ProfileService.println(sb, "mMaxConnectedAudioDevices: " + mMaxConnectedAudioDevices); 1631 if (mA2dpCodecConfig != null) { 1632 ProfileService.println(sb, "codecConfigPriorities:"); 1633 for (BluetoothCodecConfig codecConfig : mA2dpCodecConfig.codecConfigPriorities()) { 1634 ProfileService.println(sb, " " + BluetoothCodecConfig.getCodecName( 1635 codecConfig.getCodecType()) + ": " 1636 + codecConfig.getCodecPriority()); 1637 } 1638 ProfileService.println(sb, "mA2dpOffloadEnabled: " + mA2dpOffloadEnabled); 1639 if (mA2dpOffloadEnabled) { 1640 ProfileService.println(sb, "codecConfigOffloading:"); 1641 for (BluetoothCodecConfig codecConfig : mA2dpCodecConfig.codecConfigOffloading()) { 1642 ProfileService.println(sb, " " + codecConfig); 1643 } 1644 } 1645 } else { 1646 ProfileService.println(sb, "mA2dpCodecConfig: null"); 1647 } 1648 for (A2dpStateMachine sm : mStateMachines.values()) { 1649 sm.dump(sb); 1650 } 1651 } 1652 switchCodecByBufferSize(BluetoothDevice device, boolean isLowLatency)1653 public void switchCodecByBufferSize(BluetoothDevice device, boolean isLowLatency) { 1654 if (getOptionalCodecsEnabled(device) != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 1655 return; 1656 } 1657 mA2dpCodecConfig.switchCodecByBufferSize( 1658 device, isLowLatency, getCodecStatus(device).getCodecConfig().getCodecType()); 1659 } 1660 1661 /** 1662 * Sends the preferred audio profile change requested from a call to 1663 * {@link BluetoothAdapter#setPreferredAudioProfiles(BluetoothDevice, Bundle)} to the audio 1664 * framework to apply the change. The audio framework will call 1665 * {@link BluetoothAdapter#notifyActiveDeviceChangeApplied(BluetoothDevice)} once the 1666 * change is successfully applied. 1667 * 1668 * @return the number of requests sent to the audio framework 1669 */ sendPreferredAudioProfileChangeToAudioFramework()1670 public int sendPreferredAudioProfileChangeToAudioFramework() { 1671 synchronized (mStateMachines) { 1672 if (mActiveDevice == null) { 1673 Log.e(TAG, "sendPreferredAudioProfileChangeToAudioFramework: no active device"); 1674 return 0; 1675 } 1676 mAudioManager.handleBluetoothActiveDeviceChanged(mActiveDevice, mActiveDevice, 1677 BluetoothProfileConnectionInfo.createA2dpInfo(false, -1)); 1678 return 1; 1679 } 1680 } 1681 } 1682