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