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 android.bluetooth.BluetoothA2dp; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothCodecConfig; 22 import android.bluetooth.BluetoothCodecStatus; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothUuid; 26 import android.bluetooth.IBluetoothA2dp; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.media.AudioManager; 32 import android.os.HandlerThread; 33 import android.provider.Settings; 34 import android.support.annotation.GuardedBy; 35 import android.support.annotation.VisibleForTesting; 36 import android.util.Log; 37 38 import com.android.bluetooth.BluetoothMetricsProto; 39 import com.android.bluetooth.Utils; 40 import com.android.bluetooth.avrcp.Avrcp; 41 import com.android.bluetooth.avrcp.AvrcpTargetService; 42 import com.android.bluetooth.btservice.AdapterService; 43 import com.android.bluetooth.btservice.MetricsLogger; 44 import com.android.bluetooth.btservice.ProfileService; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.Objects; 49 import java.util.Set; 50 import java.util.concurrent.ConcurrentHashMap; 51 import java.util.concurrent.ConcurrentMap; 52 53 /** 54 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application. 55 * @hide 56 */ 57 public class A2dpService extends ProfileService { 58 private static final boolean DBG = true; 59 private static final String TAG = "A2dpService"; 60 61 private static A2dpService sA2dpService; 62 63 private BluetoothAdapter mAdapter; 64 private AdapterService mAdapterService; 65 private HandlerThread mStateMachinesThread; 66 private Avrcp mAvrcp; 67 68 @VisibleForTesting 69 A2dpNativeInterface mA2dpNativeInterface; 70 private AudioManager mAudioManager; 71 private A2dpCodecConfig mA2dpCodecConfig; 72 73 @GuardedBy("mStateMachines") 74 private BluetoothDevice mActiveDevice; 75 private final ConcurrentMap<BluetoothDevice, A2dpStateMachine> mStateMachines = 76 new ConcurrentHashMap<>(); 77 78 // Upper limit of all A2DP devices: Bonded or Connected 79 private static final int MAX_A2DP_STATE_MACHINES = 50; 80 // Upper limit of all A2DP devices that are Connected or Connecting 81 private int mMaxConnectedAudioDevices = 1; 82 // A2DP Offload Enabled in platform 83 boolean mA2dpOffloadEnabled = false; 84 85 private BroadcastReceiver mBondStateChangedReceiver; 86 private BroadcastReceiver mConnectionStateChangedReceiver; 87 88 @Override 89 protected IProfileServiceBinder initBinder() { 90 return new BluetoothA2dpBinder(this); 91 } 92 93 @Override 94 protected void create() { 95 Log.i(TAG, "create()"); 96 } 97 98 @Override 99 protected boolean start() { 100 Log.i(TAG, "start()"); 101 if (sA2dpService != null) { 102 throw new IllegalStateException("start() called twice"); 103 } 104 105 // Step 1: Get BluetoothAdapter, AdapterService, A2dpNativeInterface, AudioManager. 106 // None of them can be null. 107 mAdapter = Objects.requireNonNull(BluetoothAdapter.getDefaultAdapter(), 108 "BluetoothAdapter cannot be null when A2dpService starts"); 109 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 110 "AdapterService cannot be null when A2dpService starts"); 111 mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(), 112 "A2dpNativeInterface cannot be null when A2dpService starts"); 113 mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 114 Objects.requireNonNull(mAudioManager, 115 "AudioManager cannot be null when A2dpService starts"); 116 117 // Step 2: Get maximum number of connected audio devices 118 mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); 119 Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); 120 121 // Step 3: Setup AVRCP 122 mAvrcp = Avrcp.make(this); 123 124 // Step 4: Start handler thread for state machines 125 mStateMachines.clear(); 126 mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); 127 mStateMachinesThread.start(); 128 129 // Step 5: Setup codec config 130 mA2dpCodecConfig = new A2dpCodecConfig(this, mA2dpNativeInterface); 131 132 // Step 6: Initialize native interface 133 mA2dpNativeInterface.init(mMaxConnectedAudioDevices, 134 mA2dpCodecConfig.codecConfigPriorities()); 135 136 // Step 7: Check if A2DP is in offload mode 137 mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled(); 138 if (DBG) { 139 Log.d(TAG, "A2DP offload flag set to " + mA2dpOffloadEnabled); 140 } 141 142 // Step 8: Setup broadcast receivers 143 IntentFilter filter = new IntentFilter(); 144 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 145 mBondStateChangedReceiver = new BondStateChangedReceiver(); 146 registerReceiver(mBondStateChangedReceiver, filter); 147 filter = new IntentFilter(); 148 filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 149 mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver(); 150 registerReceiver(mConnectionStateChangedReceiver, filter); 151 152 // Step 9: Mark service as started 153 setA2dpService(this); 154 155 // Step 10: Clear active device 156 setActiveDevice(null); 157 158 return true; 159 } 160 161 @Override 162 protected boolean stop() { 163 Log.i(TAG, "stop()"); 164 if (sA2dpService == null) { 165 Log.w(TAG, "stop() called before start()"); 166 return true; 167 } 168 169 // Step 10: Store volume if there is an active device 170 if (mActiveDevice != null && AvrcpTargetService.get() != null) { 171 AvrcpTargetService.get().storeVolumeForDevice(mActiveDevice); 172 } 173 174 // Step 9: Clear active device and stop playing audio 175 removeActiveDevice(true); 176 177 // Step 8: Mark service as stopped 178 setA2dpService(null); 179 180 // Step 7: Unregister broadcast receivers 181 unregisterReceiver(mConnectionStateChangedReceiver); 182 mConnectionStateChangedReceiver = null; 183 unregisterReceiver(mBondStateChangedReceiver); 184 mBondStateChangedReceiver = null; 185 186 // Step 6: Cleanup native interface 187 mA2dpNativeInterface.cleanup(); 188 mA2dpNativeInterface = null; 189 190 // Step 5: Clear codec config 191 mA2dpCodecConfig = null; 192 193 // Step 4: Destroy state machines and stop handler thread 194 synchronized (mStateMachines) { 195 for (A2dpStateMachine sm : mStateMachines.values()) { 196 sm.doQuit(); 197 sm.cleanup(); 198 } 199 mStateMachines.clear(); 200 } 201 mStateMachinesThread.quitSafely(); 202 mStateMachinesThread = null; 203 204 // Step 3: Cleanup AVRCP 205 mAvrcp.doQuit(); 206 mAvrcp.cleanup(); 207 mAvrcp = null; 208 209 // Step 2: Reset maximum number of connected audio devices 210 mMaxConnectedAudioDevices = 1; 211 212 // Step 1: Clear BluetoothAdapter, AdapterService, A2dpNativeInterface, AudioManager 213 mAudioManager = null; 214 mA2dpNativeInterface = null; 215 mAdapterService = null; 216 mAdapter = null; 217 218 return true; 219 } 220 221 @Override 222 protected void cleanup() { 223 Log.i(TAG, "cleanup()"); 224 } 225 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 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 245 public boolean connect(BluetoothDevice device) { 246 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 247 if (DBG) { 248 Log.d(TAG, "connect(): " + device); 249 } 250 251 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 252 Log.e(TAG, "Cannot connect to " + device + " : PRIORITY_OFF"); 253 return false; 254 } 255 if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device), 256 BluetoothUuid.AudioSink)) { 257 Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID"); 258 return false; 259 } 260 261 synchronized (mStateMachines) { 262 if (!connectionAllowedCheckMaxDevices(device)) { 263 Log.e(TAG, "Cannot connect to " + device + " : too many connected devices"); 264 return false; 265 } 266 A2dpStateMachine smConnect = getOrCreateStateMachine(device); 267 if (smConnect == null) { 268 Log.e(TAG, "Cannot connect to " + device + " : no state machine"); 269 return false; 270 } 271 smConnect.sendMessage(A2dpStateMachine.CONNECT); 272 return true; 273 } 274 } 275 276 boolean disconnect(BluetoothDevice device) { 277 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 278 if (DBG) { 279 Log.d(TAG, "disconnect(): " + device); 280 } 281 282 synchronized (mStateMachines) { 283 A2dpStateMachine sm = mStateMachines.get(device); 284 if (sm == null) { 285 Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine"); 286 return false; 287 } 288 sm.sendMessage(A2dpStateMachine.DISCONNECT); 289 return true; 290 } 291 } 292 293 public List<BluetoothDevice> getConnectedDevices() { 294 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 295 synchronized (mStateMachines) { 296 List<BluetoothDevice> devices = new ArrayList<>(); 297 for (A2dpStateMachine sm : mStateMachines.values()) { 298 if (sm.isConnected()) { 299 devices.add(sm.getDevice()); 300 } 301 } 302 return devices; 303 } 304 } 305 306 /** 307 * Check whether can connect to a peer device. 308 * The check considers the maximum number of connected peers. 309 * 310 * @param device the peer device to connect to 311 * @return true if connection is allowed, otherwise false 312 */ 313 private boolean connectionAllowedCheckMaxDevices(BluetoothDevice device) { 314 int connected = 0; 315 // Count devices that are in the process of connecting or already connected 316 synchronized (mStateMachines) { 317 for (A2dpStateMachine sm : mStateMachines.values()) { 318 switch (sm.getConnectionState()) { 319 case BluetoothProfile.STATE_CONNECTING: 320 case BluetoothProfile.STATE_CONNECTED: 321 if (Objects.equals(device, sm.getDevice())) { 322 return true; // Already connected or accounted for 323 } 324 connected++; 325 break; 326 default: 327 break; 328 } 329 } 330 } 331 return (connected < mMaxConnectedAudioDevices); 332 } 333 334 /** 335 * Check whether can connect to a peer device. 336 * The check considers a number of factors during the evaluation. 337 * 338 * @param device the peer device to connect to 339 * @param isOutgoingRequest if true, the check is for outgoing connection 340 * request, otherwise is for incoming connection request 341 * @return true if connection is allowed, otherwise false 342 */ 343 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) 344 public boolean okToConnect(BluetoothDevice device, boolean isOutgoingRequest) { 345 Log.i(TAG, "okToConnect: device " + device + " isOutgoingRequest: " + isOutgoingRequest); 346 // Check if this is an incoming connection in Quiet mode. 347 if (mAdapterService.isQuietModeEnabled() && !isOutgoingRequest) { 348 Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled"); 349 return false; 350 } 351 // Check if too many devices 352 if (!connectionAllowedCheckMaxDevices(device)) { 353 Log.e(TAG, "okToConnect: cannot connect to " + device 354 + " : too many connected devices"); 355 return false; 356 } 357 // Check priority and accept or reject the connection. 358 // Note: Logic can be simplified, but keeping it this way for readability 359 int priority = getPriority(device); 360 int bondState = mAdapterService.getBondState(device); 361 // If priority is undefined, it is likely that service discovery has not completed and peer 362 // initiated the connection. Allow this connection only if the device is bonded or bonding 363 boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED) 364 && (bondState == BluetoothDevice.BOND_BONDING 365 || bondState == BluetoothDevice.BOND_BONDED); 366 // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT. 367 boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON 368 || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT) 369 && (bondState == BluetoothDevice.BOND_BONDED 370 || bondState == BluetoothDevice.BOND_BONDING); 371 if (!serviceDiscoveryPending && !isEnabled) { 372 // Otherwise, reject the connection if no service discovery is pending and priority is 373 // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT 374 Log.w(TAG, "okToConnect: return false, priority=" + priority + ", bondState=" 375 + bondState); 376 return false; 377 } 378 return true; 379 } 380 381 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 382 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 383 List<BluetoothDevice> devices = new ArrayList<>(); 384 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 385 synchronized (mStateMachines) { 386 for (BluetoothDevice device : bondedDevices) { 387 if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device), 388 BluetoothUuid.AudioSink)) { 389 continue; 390 } 391 int connectionState = BluetoothProfile.STATE_DISCONNECTED; 392 A2dpStateMachine sm = mStateMachines.get(device); 393 if (sm != null) { 394 connectionState = sm.getConnectionState(); 395 } 396 for (int i = 0; i < states.length; i++) { 397 if (connectionState == states[i]) { 398 devices.add(device); 399 } 400 } 401 } 402 return devices; 403 } 404 } 405 406 /** 407 * Get the list of devices that have state machines. 408 * 409 * @return the list of devices that have state machines 410 */ 411 @VisibleForTesting 412 List<BluetoothDevice> getDevices() { 413 List<BluetoothDevice> devices = new ArrayList<>(); 414 synchronized (mStateMachines) { 415 for (A2dpStateMachine sm : mStateMachines.values()) { 416 devices.add(sm.getDevice()); 417 } 418 return devices; 419 } 420 } 421 422 public int getConnectionState(BluetoothDevice device) { 423 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 424 synchronized (mStateMachines) { 425 A2dpStateMachine sm = mStateMachines.get(device); 426 if (sm == null) { 427 return BluetoothProfile.STATE_DISCONNECTED; 428 } 429 return sm.getConnectionState(); 430 } 431 } 432 433 private void removeActiveDevice(boolean forceStopPlayingAudio) { 434 BluetoothDevice previousActiveDevice = mActiveDevice; 435 synchronized (mStateMachines) { 436 // Clear the active device 437 mActiveDevice = null; 438 // This needs to happen before we inform the audio manager that the device 439 // disconnected. Please see comment in broadcastActiveDevice() for why. 440 broadcastActiveDevice(null); 441 442 if (previousActiveDevice == null) { 443 return; 444 } 445 446 // Make sure the Audio Manager knows the previous Active device is disconnected. 447 // However, if A2DP is still connected and not forcing stop audio for that remote 448 // device, the user has explicitly switched the output to the local device and music 449 // should continue playing. Otherwise, the remote device has been indeed disconnected 450 // and audio should be suspended before switching the output to the local device. 451 boolean suppressNoisyIntent = !forceStopPlayingAudio 452 && (getConnectionState(previousActiveDevice) 453 == BluetoothProfile.STATE_CONNECTED); 454 Log.i(TAG, "removeActiveDevice: suppressNoisyIntent=" + suppressNoisyIntent); 455 mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( 456 previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED, 457 BluetoothProfile.A2DP, suppressNoisyIntent, -1); 458 // Make sure the Active device in native layer is set to null and audio is off 459 if (!mA2dpNativeInterface.setActiveDevice(null)) { 460 Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native " 461 + "layer"); 462 } 463 } 464 } 465 466 /** 467 * Set the active device. 468 * 469 * @param device the active device 470 * @return true on success, otherwise false 471 */ 472 public boolean setActiveDevice(BluetoothDevice device) { 473 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 474 synchronized (mStateMachines) { 475 BluetoothDevice previousActiveDevice = mActiveDevice; 476 if (DBG) { 477 Log.d(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice); 478 } 479 480 if (previousActiveDevice != null && AvrcpTargetService.get() != null) { 481 AvrcpTargetService.get().storeVolumeForDevice(previousActiveDevice); 482 } 483 484 if (device == null) { 485 // Remove active device and continue playing audio only if necessary. 486 removeActiveDevice(false); 487 return true; 488 } 489 490 BluetoothCodecStatus codecStatus = null; 491 A2dpStateMachine sm = mStateMachines.get(device); 492 if (sm == null) { 493 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: " 494 + "no state machine"); 495 return false; 496 } 497 if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 498 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: " 499 + "device is not connected"); 500 return false; 501 } 502 if (!mA2dpNativeInterface.setActiveDevice(device)) { 503 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer"); 504 return false; 505 } 506 codecStatus = sm.getCodecStatus(); 507 508 boolean deviceChanged = !Objects.equals(device, mActiveDevice); 509 mActiveDevice = device; 510 // This needs to happen before we inform the audio manager that the device 511 // disconnected. Please see comment in broadcastActiveDevice() for why. 512 broadcastActiveDevice(mActiveDevice); 513 if (deviceChanged) { 514 // Send an intent with the active device codec config 515 if (codecStatus != null) { 516 broadcastCodecConfig(mActiveDevice, codecStatus); 517 } 518 // Make sure the Audio Manager knows the previous Active device is disconnected, 519 // and the new Active device is connected. 520 if (previousActiveDevice != null) { 521 mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( 522 previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED, 523 BluetoothProfile.A2DP, true, -1); 524 } 525 526 int rememberedVolume = -1; 527 if (AvrcpTargetService.get() != null) { 528 AvrcpTargetService.get().volumeDeviceSwitched(mActiveDevice); 529 530 rememberedVolume = AvrcpTargetService.get() 531 .getRememberedVolumeForDevice(mActiveDevice); 532 } 533 534 mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( 535 mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, 536 true, rememberedVolume); 537 538 // Inform the Audio Service about the codec configuration 539 // change, so the Audio Service can reset accordingly the audio 540 // feeding parameters in the Audio HAL to the Bluetooth stack. 541 mAudioManager.handleBluetoothA2dpDeviceConfigChange(mActiveDevice); 542 } 543 } 544 return true; 545 } 546 547 /** 548 * Get the active device. 549 * 550 * @return the active device or null if no device is active 551 */ 552 public BluetoothDevice getActiveDevice() { 553 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 554 synchronized (mStateMachines) { 555 return mActiveDevice; 556 } 557 } 558 559 private boolean isActiveDevice(BluetoothDevice device) { 560 synchronized (mStateMachines) { 561 return (device != null) && Objects.equals(device, mActiveDevice); 562 } 563 } 564 565 public boolean setPriority(BluetoothDevice device, int priority) { 566 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 567 Settings.Global.putInt(getContentResolver(), 568 Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority); 569 if (DBG) { 570 Log.d(TAG, "Saved priority " + device + " = " + priority); 571 } 572 return true; 573 } 574 575 public int getPriority(BluetoothDevice device) { 576 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 577 int priority = Settings.Global.getInt(getContentResolver(), 578 Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), 579 BluetoothProfile.PRIORITY_UNDEFINED); 580 return priority; 581 } 582 583 /* Absolute volume implementation */ 584 public boolean isAvrcpAbsoluteVolumeSupported() { 585 return mAvrcp.isAbsoluteVolumeSupported(); 586 } 587 588 public void setAvrcpAbsoluteVolume(int volume) { 589 // TODO (apanicke): Instead of using A2DP as a middleman for volume changes, add a binder 590 // service to the new AVRCP Profile and have the audio manager use that instead. 591 if (AvrcpTargetService.get() != null) { 592 AvrcpTargetService.get().sendVolumeChanged(volume); 593 return; 594 } 595 596 mAvrcp.setAbsoluteVolume(volume); 597 } 598 599 public void setAvrcpAudioState(int state) { 600 mAvrcp.setA2dpAudioState(state); 601 } 602 603 public void resetAvrcpBlacklist(BluetoothDevice device) { 604 if (mAvrcp != null) { 605 mAvrcp.resetBlackList(device.getAddress()); 606 } 607 } 608 609 boolean isA2dpPlaying(BluetoothDevice device) { 610 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 611 if (DBG) { 612 Log.d(TAG, "isA2dpPlaying(" + device + ")"); 613 } 614 synchronized (mStateMachines) { 615 A2dpStateMachine sm = mStateMachines.get(device); 616 if (sm == null) { 617 return false; 618 } 619 return sm.isPlaying(); 620 } 621 } 622 623 /** 624 * Gets the current codec status (configuration and capability). 625 * 626 * @param device the remote Bluetooth device. If null, use the currect 627 * active A2DP Bluetooth device. 628 * @return the current codec status 629 * @hide 630 */ 631 public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { 632 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 633 if (DBG) { 634 Log.d(TAG, "getCodecStatus(" + device + ")"); 635 } 636 synchronized (mStateMachines) { 637 if (device == null) { 638 device = mActiveDevice; 639 } 640 if (device == null) { 641 return null; 642 } 643 A2dpStateMachine sm = mStateMachines.get(device); 644 if (sm != null) { 645 return sm.getCodecStatus(); 646 } 647 return null; 648 } 649 } 650 651 /** 652 * Sets the codec configuration preference. 653 * 654 * @param device the remote Bluetooth device. If null, use the currect 655 * active A2DP Bluetooth device. 656 * @param codecConfig the codec configuration preference 657 * @hide 658 */ 659 public void setCodecConfigPreference(BluetoothDevice device, 660 BluetoothCodecConfig codecConfig) { 661 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 662 if (DBG) { 663 Log.d(TAG, "setCodecConfigPreference(" + device + "): " 664 + Objects.toString(codecConfig)); 665 } 666 if (device == null) { 667 device = mActiveDevice; 668 } 669 if (device == null) { 670 Log.e(TAG, "Cannot set codec config preference: no active A2DP device"); 671 return; 672 } 673 mA2dpCodecConfig.setCodecConfigPreference(device, codecConfig); 674 } 675 676 /** 677 * Enables the optional codecs. 678 * 679 * @param device the remote Bluetooth device. If null, use the currect 680 * active A2DP Bluetooth device. 681 * @hide 682 */ 683 public void enableOptionalCodecs(BluetoothDevice device) { 684 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 685 if (DBG) { 686 Log.d(TAG, "enableOptionalCodecs(" + device + ")"); 687 } 688 if (device == null) { 689 device = mActiveDevice; 690 } 691 if (device == null) { 692 Log.e(TAG, "Cannot enable optional codecs: no active A2DP device"); 693 return; 694 } 695 mA2dpCodecConfig.enableOptionalCodecs(device); 696 } 697 698 /** 699 * Disables the optional codecs. 700 * 701 * @param device the remote Bluetooth device. If null, use the currect 702 * active A2DP Bluetooth device. 703 * @hide 704 */ 705 public void disableOptionalCodecs(BluetoothDevice device) { 706 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 707 if (DBG) { 708 Log.d(TAG, "disableOptionalCodecs(" + device + ")"); 709 } 710 if (device == null) { 711 device = mActiveDevice; 712 } 713 if (device == null) { 714 Log.e(TAG, "Cannot disable optional codecs: no active A2DP device"); 715 return; 716 } 717 mA2dpCodecConfig.disableOptionalCodecs(device); 718 } 719 720 public int getSupportsOptionalCodecs(BluetoothDevice device) { 721 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 722 int support = Settings.Global.getInt(getContentResolver(), 723 Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()), 724 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN); 725 return support; 726 } 727 728 public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) { 729 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 730 int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED 731 : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED; 732 Settings.Global.putInt(getContentResolver(), 733 Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()), 734 value); 735 } 736 737 public int getOptionalCodecsEnabled(BluetoothDevice device) { 738 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 739 return Settings.Global.getInt(getContentResolver(), 740 Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()), 741 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN); 742 } 743 744 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { 745 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 746 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN 747 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED 748 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 749 Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value); 750 return; 751 } 752 Settings.Global.putInt(getContentResolver(), 753 Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()), 754 value); 755 } 756 757 // Handle messages from native (JNI) to Java 758 void messageFromNative(A2dpStackEvent stackEvent) { 759 Objects.requireNonNull(stackEvent.device, 760 "Device should never be null, event: " + stackEvent); 761 synchronized (mStateMachines) { 762 BluetoothDevice device = stackEvent.device; 763 A2dpStateMachine sm = mStateMachines.get(device); 764 if (sm == null) { 765 if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 766 switch (stackEvent.valueInt) { 767 case A2dpStackEvent.CONNECTION_STATE_CONNECTED: 768 case A2dpStackEvent.CONNECTION_STATE_CONNECTING: 769 // Create a new state machine only when connecting to a device 770 if (!connectionAllowedCheckMaxDevices(device)) { 771 Log.e(TAG, "Cannot connect to " + device 772 + " : too many connected devices"); 773 return; 774 } 775 sm = getOrCreateStateMachine(device); 776 break; 777 default: 778 break; 779 } 780 } 781 } 782 if (sm == null) { 783 Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); 784 return; 785 } 786 sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); 787 } 788 } 789 790 /** 791 * The codec configuration for a device has been updated. 792 * 793 * @param device the remote device 794 * @param codecStatus the new codec status 795 * @param sameAudioFeedingParameters if true the audio feeding parameters 796 * haven't been changed 797 */ 798 void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, 799 boolean sameAudioFeedingParameters) { 800 broadcastCodecConfig(device, codecStatus); 801 802 // Inform the Audio Service about the codec configuration change, 803 // so the Audio Service can reset accordingly the audio feeding 804 // parameters in the Audio HAL to the Bluetooth stack. 805 if (isActiveDevice(device) && !sameAudioFeedingParameters) { 806 mAudioManager.handleBluetoothA2dpDeviceConfigChange(device); 807 } 808 } 809 810 private A2dpStateMachine getOrCreateStateMachine(BluetoothDevice device) { 811 if (device == null) { 812 Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null"); 813 return null; 814 } 815 synchronized (mStateMachines) { 816 A2dpStateMachine sm = mStateMachines.get(device); 817 if (sm != null) { 818 return sm; 819 } 820 // Limit the maximum number of state machines to avoid DoS attack 821 if (mStateMachines.size() >= MAX_A2DP_STATE_MACHINES) { 822 Log.e(TAG, "Maximum number of A2DP state machines reached: " 823 + MAX_A2DP_STATE_MACHINES); 824 return null; 825 } 826 if (DBG) { 827 Log.d(TAG, "Creating a new state machine for " + device); 828 } 829 sm = A2dpStateMachine.make(device, this, mA2dpNativeInterface, 830 mStateMachinesThread.getLooper()); 831 mStateMachines.put(device, sm); 832 return sm; 833 } 834 } 835 836 private void broadcastActiveDevice(BluetoothDevice device) { 837 if (DBG) { 838 Log.d(TAG, "broadcastActiveDevice(" + device + ")"); 839 } 840 841 Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); 842 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 843 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 844 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 845 sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 846 } 847 848 private void broadcastCodecConfig(BluetoothDevice device, BluetoothCodecStatus codecStatus) { 849 if (DBG) { 850 Log.d(TAG, "broadcastCodecConfig(" + device + "): " + codecStatus); 851 } 852 Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED); 853 intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, codecStatus); 854 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 855 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 856 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 857 sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 858 } 859 860 private class BondStateChangedReceiver extends BroadcastReceiver { 861 @Override 862 public void onReceive(Context context, Intent intent) { 863 if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { 864 return; 865 } 866 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 867 BluetoothDevice.ERROR); 868 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 869 Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 870 bondStateChanged(device, state); 871 } 872 } 873 874 /** 875 * Process a change in the bonding state for a device. 876 * 877 * @param device the device whose bonding state has changed 878 * @param bondState the new bond state for the device. Possible values are: 879 * {@link BluetoothDevice#BOND_NONE}, 880 * {@link BluetoothDevice#BOND_BONDING}, 881 * {@link BluetoothDevice#BOND_BONDED}. 882 */ 883 @VisibleForTesting 884 void bondStateChanged(BluetoothDevice device, int bondState) { 885 if (DBG) { 886 Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState); 887 } 888 // Remove state machine if the bonding for a device is removed 889 if (bondState != BluetoothDevice.BOND_NONE) { 890 return; 891 } 892 synchronized (mStateMachines) { 893 A2dpStateMachine sm = mStateMachines.get(device); 894 if (sm == null) { 895 return; 896 } 897 if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 898 return; 899 } 900 removeStateMachine(device); 901 } 902 } 903 904 private void removeStateMachine(BluetoothDevice device) { 905 synchronized (mStateMachines) { 906 A2dpStateMachine sm = mStateMachines.get(device); 907 if (sm == null) { 908 Log.w(TAG, "removeStateMachine: device " + device 909 + " does not have a state machine"); 910 return; 911 } 912 Log.i(TAG, "removeStateMachine: removing state machine for device: " + device); 913 sm.doQuit(); 914 sm.cleanup(); 915 mStateMachines.remove(device); 916 } 917 } 918 919 private void updateOptionalCodecsSupport(BluetoothDevice device) { 920 int previousSupport = getSupportsOptionalCodecs(device); 921 boolean supportsOptional = false; 922 923 synchronized (mStateMachines) { 924 A2dpStateMachine sm = mStateMachines.get(device); 925 if (sm == null) { 926 return; 927 } 928 BluetoothCodecStatus codecStatus = sm.getCodecStatus(); 929 if (codecStatus != null) { 930 for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) { 931 if (!config.isMandatoryCodec()) { 932 supportsOptional = true; 933 break; 934 } 935 } 936 } 937 } 938 if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN 939 || supportsOptional != (previousSupport 940 == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { 941 setSupportsOptionalCodecs(device, supportsOptional); 942 } 943 if (supportsOptional) { 944 int enabled = getOptionalCodecsEnabled(device); 945 if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 946 enableOptionalCodecs(device); 947 } else if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED) { 948 disableOptionalCodecs(device); 949 } 950 } 951 } 952 953 private void connectionStateChanged(BluetoothDevice device, int fromState, int toState) { 954 if ((device == null) || (fromState == toState)) { 955 return; 956 } 957 synchronized (mStateMachines) { 958 if (toState == BluetoothProfile.STATE_CONNECTED) { 959 // Each time a device connects, we want to re-check if it supports optional 960 // codecs (perhaps it's had a firmware update, etc.) and save that state if 961 // it differs from what we had saved before. 962 updateOptionalCodecsSupport(device); 963 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); 964 } 965 // Set the active device if only one connected device is supported and it was connected 966 if (toState == BluetoothProfile.STATE_CONNECTED && (mMaxConnectedAudioDevices == 1)) { 967 setActiveDevice(device); 968 } 969 // Check if the active device is not connected anymore 970 if (isActiveDevice(device) && (fromState == BluetoothProfile.STATE_CONNECTED)) { 971 setActiveDevice(null); 972 } 973 // Check if the device is disconnected - if unbond, remove the state machine 974 if (toState == BluetoothProfile.STATE_DISCONNECTED) { 975 int bondState = mAdapterService.getBondState(device); 976 if (bondState == BluetoothDevice.BOND_NONE) { 977 removeStateMachine(device); 978 } 979 } 980 } 981 } 982 983 /** 984 * Receiver for processing device connection state changes. 985 * 986 * <ul> 987 * <li> Update codec support per device when device is (re)connected 988 * <li> Delete the state machine instance if the device is disconnected and unbond 989 * </ul> 990 */ 991 private class ConnectionStateChangedReceiver extends BroadcastReceiver { 992 @Override 993 public void onReceive(Context context, Intent intent) { 994 if (!BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { 995 return; 996 } 997 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 998 int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 999 int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); 1000 connectionStateChanged(device, fromState, toState); 1001 } 1002 } 1003 1004 /** 1005 * Binder object: must be a static class or memory leak may occur. 1006 */ 1007 @VisibleForTesting 1008 static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 1009 implements IProfileServiceBinder { 1010 private A2dpService mService; 1011 1012 private A2dpService getService() { 1013 if (!Utils.checkCaller()) { 1014 Log.w(TAG, "A2DP call not allowed for non-active user"); 1015 return null; 1016 } 1017 1018 if (mService != null && mService.isAvailable()) { 1019 return mService; 1020 } 1021 return null; 1022 } 1023 1024 BluetoothA2dpBinder(A2dpService svc) { 1025 mService = svc; 1026 } 1027 1028 @Override 1029 public void cleanup() { 1030 mService = null; 1031 } 1032 1033 @Override 1034 public boolean connect(BluetoothDevice device) { 1035 A2dpService service = getService(); 1036 if (service == null) { 1037 return false; 1038 } 1039 return service.connect(device); 1040 } 1041 1042 @Override 1043 public boolean disconnect(BluetoothDevice device) { 1044 A2dpService service = getService(); 1045 if (service == null) { 1046 return false; 1047 } 1048 return service.disconnect(device); 1049 } 1050 1051 @Override 1052 public List<BluetoothDevice> getConnectedDevices() { 1053 A2dpService service = getService(); 1054 if (service == null) { 1055 return new ArrayList<>(0); 1056 } 1057 return service.getConnectedDevices(); 1058 } 1059 1060 @Override 1061 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1062 A2dpService service = getService(); 1063 if (service == null) { 1064 return new ArrayList<>(0); 1065 } 1066 return service.getDevicesMatchingConnectionStates(states); 1067 } 1068 1069 @Override 1070 public int getConnectionState(BluetoothDevice device) { 1071 A2dpService service = getService(); 1072 if (service == null) { 1073 return BluetoothProfile.STATE_DISCONNECTED; 1074 } 1075 return service.getConnectionState(device); 1076 } 1077 1078 @Override 1079 public boolean setActiveDevice(BluetoothDevice device) { 1080 A2dpService service = getService(); 1081 if (service == null) { 1082 return false; 1083 } 1084 return service.setActiveDevice(device); 1085 } 1086 1087 @Override 1088 public BluetoothDevice getActiveDevice() { 1089 A2dpService service = getService(); 1090 if (service == null) { 1091 return null; 1092 } 1093 return service.getActiveDevice(); 1094 } 1095 1096 @Override 1097 public boolean setPriority(BluetoothDevice device, int priority) { 1098 A2dpService service = getService(); 1099 if (service == null) { 1100 return false; 1101 } 1102 return service.setPriority(device, priority); 1103 } 1104 1105 @Override 1106 public int getPriority(BluetoothDevice device) { 1107 A2dpService service = getService(); 1108 if (service == null) { 1109 return BluetoothProfile.PRIORITY_UNDEFINED; 1110 } 1111 return service.getPriority(device); 1112 } 1113 1114 @Override 1115 public boolean isAvrcpAbsoluteVolumeSupported() { 1116 A2dpService service = getService(); 1117 if (service == null) { 1118 return false; 1119 } 1120 return service.isAvrcpAbsoluteVolumeSupported(); 1121 } 1122 1123 @Override 1124 public void setAvrcpAbsoluteVolume(int volume) { 1125 A2dpService service = getService(); 1126 if (service == null) { 1127 return; 1128 } 1129 service.setAvrcpAbsoluteVolume(volume); 1130 } 1131 1132 @Override 1133 public boolean isA2dpPlaying(BluetoothDevice device) { 1134 A2dpService service = getService(); 1135 if (service == null) { 1136 return false; 1137 } 1138 return service.isA2dpPlaying(device); 1139 } 1140 1141 @Override 1142 public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { 1143 A2dpService service = getService(); 1144 if (service == null) { 1145 return null; 1146 } 1147 return service.getCodecStatus(device); 1148 } 1149 1150 @Override 1151 public void setCodecConfigPreference(BluetoothDevice device, 1152 BluetoothCodecConfig codecConfig) { 1153 A2dpService service = getService(); 1154 if (service == null) { 1155 return; 1156 } 1157 service.setCodecConfigPreference(device, codecConfig); 1158 } 1159 1160 @Override 1161 public void enableOptionalCodecs(BluetoothDevice device) { 1162 A2dpService service = getService(); 1163 if (service == null) { 1164 return; 1165 } 1166 service.enableOptionalCodecs(device); 1167 } 1168 1169 @Override 1170 public void disableOptionalCodecs(BluetoothDevice device) { 1171 A2dpService service = getService(); 1172 if (service == null) { 1173 return; 1174 } 1175 service.disableOptionalCodecs(device); 1176 } 1177 1178 public int supportsOptionalCodecs(BluetoothDevice device) { 1179 A2dpService service = getService(); 1180 if (service == null) { 1181 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; 1182 } 1183 return service.getSupportsOptionalCodecs(device); 1184 } 1185 1186 public int getOptionalCodecsEnabled(BluetoothDevice device) { 1187 A2dpService service = getService(); 1188 if (service == null) { 1189 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; 1190 } 1191 return service.getOptionalCodecsEnabled(device); 1192 } 1193 1194 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { 1195 A2dpService service = getService(); 1196 if (service == null) { 1197 return; 1198 } 1199 service.setOptionalCodecsEnabled(device, value); 1200 } 1201 } 1202 1203 @Override 1204 public void dump(StringBuilder sb) { 1205 super.dump(sb); 1206 ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); 1207 for (A2dpStateMachine sm : mStateMachines.values()) { 1208 sm.dump(sb); 1209 } 1210 if (mAvrcp != null) { 1211 mAvrcp.dump(sb); 1212 } 1213 } 1214 } 1215