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