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