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