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