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