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