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