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