1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.bluetooth; 18 19 import android.Manifest; 20 import android.annotation.RequiresPermission; 21 import android.annotation.SdkConstant; 22 import android.annotation.SdkConstant.SdkConstantType; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.ServiceConnection; 27 import android.media.AudioManager; 28 import android.os.Binder; 29 import android.os.IBinder; 30 import android.os.ParcelUuid; 31 import android.os.RemoteException; 32 import android.util.Log; 33 34 import com.android.internal.annotations.GuardedBy; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.concurrent.locks.ReentrantReadWriteLock; 39 40 41 /** 42 * This class provides the public APIs to control the Bluetooth A2DP 43 * profile. 44 * 45 *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP 46 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 47 * the BluetoothA2dp proxy object. 48 * 49 * <p> Android only supports one connected Bluetooth A2dp device at a time. 50 * Each method is protected with its appropriate permission. 51 */ 52 public final class BluetoothA2dp implements BluetoothProfile { 53 private static final String TAG = "BluetoothA2dp"; 54 private static final boolean DBG = true; 55 private static final boolean VDBG = false; 56 57 /** 58 * Intent used to broadcast the change in connection state of the A2DP 59 * profile. 60 * 61 * <p>This intent will have 3 extras: 62 * <ul> 63 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 64 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> 65 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 66 * </ul> 67 * 68 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 69 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 70 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 71 * 72 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 73 * receive. 74 */ 75 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 76 public static final String ACTION_CONNECTION_STATE_CHANGED = 77 "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED"; 78 79 /** 80 * Intent used to broadcast the change in the Playing state of the A2DP 81 * profile. 82 * 83 * <p>This intent will have 3 extras: 84 * <ul> 85 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 86 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> 87 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 88 * </ul> 89 * 90 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 91 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, 92 * 93 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 94 * receive. 95 */ 96 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 97 public static final String ACTION_PLAYING_STATE_CHANGED = 98 "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; 99 100 /** @hide */ 101 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 102 public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = 103 "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; 104 105 /** 106 * Intent used to broadcast the change in the Audio Codec state of the 107 * A2DP Source profile. 108 * 109 * <p>This intent will have 2 extras: 110 * <ul> 111 * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li> 112 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently 113 * connected, otherwise it is not included.</li> 114 * </ul> 115 * 116 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 117 * receive. 118 * 119 * @hide 120 */ 121 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 122 public static final String ACTION_CODEC_CONFIG_CHANGED = 123 "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; 124 125 /** 126 * A2DP sink device is streaming music. This state can be one of 127 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of 128 * {@link #ACTION_PLAYING_STATE_CHANGED} intent. 129 */ 130 public static final int STATE_PLAYING = 10; 131 132 /** 133 * A2DP sink device is NOT streaming music. This state can be one of 134 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of 135 * {@link #ACTION_PLAYING_STATE_CHANGED} intent. 136 */ 137 public static final int STATE_NOT_PLAYING = 11; 138 139 /** 140 * We don't have a stored preference for whether or not the given A2DP sink device supports 141 * optional codecs. 142 * @hide */ 143 public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; 144 145 /** 146 * The given A2DP sink device does not support optional codecs. 147 * @hide */ 148 public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; 149 150 /** 151 * The given A2DP sink device does support optional codecs. 152 * @hide */ 153 public static final int OPTIONAL_CODECS_SUPPORTED = 1; 154 155 /** 156 * We don't have a stored preference for whether optional codecs should be enabled or disabled 157 * for the given A2DP device. 158 * @hide */ 159 public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; 160 161 /** 162 * Optional codecs should be disabled for the given A2DP device. 163 * @hide */ 164 public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; 165 166 /** 167 * Optional codecs should be enabled for the given A2DP device. 168 * @hide */ 169 public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; 170 171 private Context mContext; 172 private ServiceListener mServiceListener; 173 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); 174 @GuardedBy("mServiceLock") private IBluetoothA2dp mService; 175 private BluetoothAdapter mAdapter; 176 177 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 178 new IBluetoothStateChangeCallback.Stub() { 179 public void onBluetoothStateChange(boolean up) { 180 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); 181 if (!up) { 182 if (VDBG) Log.d(TAG, "Unbinding service..."); 183 try { 184 mServiceLock.writeLock().lock(); 185 mService = null; 186 mContext.unbindService(mConnection); 187 } catch (Exception re) { 188 Log.e(TAG, "", re); 189 } finally { 190 mServiceLock.writeLock().unlock(); 191 } 192 } else { 193 try { 194 mServiceLock.readLock().lock(); 195 if (mService == null) { 196 if (VDBG) Log.d(TAG,"Binding service..."); 197 doBind(); 198 } 199 } catch (Exception re) { 200 Log.e(TAG,"",re); 201 } finally { 202 mServiceLock.readLock().unlock(); 203 } 204 } 205 } 206 }; 207 /** 208 * Create a BluetoothA2dp proxy object for interacting with the local 209 * Bluetooth A2DP service. 210 * 211 */ BluetoothA2dp(Context context, ServiceListener l)212 /*package*/ BluetoothA2dp(Context context, ServiceListener l) { 213 mContext = context; 214 mServiceListener = l; 215 mAdapter = BluetoothAdapter.getDefaultAdapter(); 216 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 217 if (mgr != null) { 218 try { 219 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 220 } catch (RemoteException e) { 221 Log.e(TAG,"",e); 222 } 223 } 224 225 doBind(); 226 } 227 doBind()228 boolean doBind() { 229 Intent intent = new Intent(IBluetoothA2dp.class.getName()); 230 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 231 intent.setComponent(comp); 232 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, 233 android.os.Process.myUserHandle())) { 234 Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); 235 return false; 236 } 237 return true; 238 } 239 close()240 /*package*/ void close() { 241 mServiceListener = null; 242 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 243 if (mgr != null) { 244 try { 245 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 246 } catch (Exception e) { 247 Log.e(TAG,"",e); 248 } 249 } 250 251 try { 252 mServiceLock.writeLock().lock(); 253 if (mService != null) { 254 mService = null; 255 mContext.unbindService(mConnection); 256 } 257 } catch (Exception re) { 258 Log.e(TAG, "", re); 259 } finally { 260 mServiceLock.writeLock().unlock(); 261 } 262 } 263 finalize()264 public void finalize() { 265 // The empty finalize needs to be kept or the 266 // cts signature tests would fail. 267 } 268 /** 269 * Initiate connection to a profile of the remote bluetooth device. 270 * 271 * <p> Currently, the system supports only 1 connection to the 272 * A2DP profile. The API will automatically disconnect connected 273 * devices before connecting. 274 * 275 * <p> This API returns false in scenarios like the profile on the 276 * device is already connected or Bluetooth is not turned on. 277 * When this API returns true, it is guaranteed that 278 * connection state intent for the profile will be broadcasted with 279 * the state. Users can get the connection state of the profile 280 * from this intent. 281 * 282 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 283 * permission. 284 * 285 * @param device Remote Bluetooth Device 286 * @return false on immediate error, 287 * true otherwise 288 * @hide 289 */ connect(BluetoothDevice device)290 public boolean connect(BluetoothDevice device) { 291 if (DBG) log("connect(" + device + ")"); 292 try { 293 mServiceLock.readLock().lock(); 294 if (mService != null && isEnabled() && 295 isValidDevice(device)) { 296 return mService.connect(device); 297 } 298 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 299 return false; 300 } catch (RemoteException e) { 301 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 302 return false; 303 } finally { 304 mServiceLock.readLock().unlock(); 305 } 306 } 307 308 /** 309 * Initiate disconnection from a profile 310 * 311 * <p> This API will return false in scenarios like the profile on the 312 * Bluetooth device is not in connected state etc. When this API returns, 313 * true, it is guaranteed that the connection state change 314 * intent will be broadcasted with the state. Users can get the 315 * disconnection state of the profile from this intent. 316 * 317 * <p> If the disconnection is initiated by a remote device, the state 318 * will transition from {@link #STATE_CONNECTED} to 319 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the 320 * host (local) device the state will transition from 321 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to 322 * state {@link #STATE_DISCONNECTED}. The transition to 323 * {@link #STATE_DISCONNECTING} can be used to distinguish between the 324 * two scenarios. 325 * 326 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 327 * permission. 328 * 329 * @param device Remote Bluetooth Device 330 * @return false on immediate error, 331 * true otherwise 332 * @hide 333 */ disconnect(BluetoothDevice device)334 public boolean disconnect(BluetoothDevice device) { 335 if (DBG) log("disconnect(" + device + ")"); 336 try { 337 mServiceLock.readLock().lock(); 338 if (mService != null && isEnabled() && 339 isValidDevice(device)) { 340 return mService.disconnect(device); 341 } 342 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 343 return false; 344 } catch (RemoteException e) { 345 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 346 return false; 347 } finally { 348 mServiceLock.readLock().unlock(); 349 } 350 } 351 352 /** 353 * {@inheritDoc} 354 */ getConnectedDevices()355 public List<BluetoothDevice> getConnectedDevices() { 356 if (VDBG) log("getConnectedDevices()"); 357 try { 358 mServiceLock.readLock().lock(); 359 if (mService != null && isEnabled()) { 360 return mService.getConnectedDevices(); 361 } 362 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 363 return new ArrayList<BluetoothDevice>(); 364 } catch (RemoteException e) { 365 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 366 return new ArrayList<BluetoothDevice>(); 367 } finally { 368 mServiceLock.readLock().unlock(); 369 } 370 } 371 372 /** 373 * {@inheritDoc} 374 */ getDevicesMatchingConnectionStates(int[] states)375 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 376 if (VDBG) log("getDevicesMatchingStates()"); 377 try { 378 mServiceLock.readLock().lock(); 379 if (mService != null && isEnabled()) { 380 return mService.getDevicesMatchingConnectionStates(states); 381 } 382 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 383 return new ArrayList<BluetoothDevice>(); 384 } catch (RemoteException e) { 385 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 386 return new ArrayList<BluetoothDevice>(); 387 } finally { 388 mServiceLock.readLock().unlock(); 389 } 390 } 391 392 /** 393 * {@inheritDoc} 394 */ getConnectionState(BluetoothDevice device)395 public int getConnectionState(BluetoothDevice device) { 396 if (VDBG) log("getState(" + device + ")"); 397 try { 398 mServiceLock.readLock().lock(); 399 if (mService != null && isEnabled() 400 && isValidDevice(device)) { 401 return mService.getConnectionState(device); 402 } 403 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 404 return BluetoothProfile.STATE_DISCONNECTED; 405 } catch (RemoteException e) { 406 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 407 return BluetoothProfile.STATE_DISCONNECTED; 408 } finally { 409 mServiceLock.readLock().unlock(); 410 } 411 } 412 413 /** 414 * Set priority of the profile 415 * 416 * <p> The device should already be paired. 417 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager 418 * {@link #PRIORITY_OFF}, 419 * 420 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 421 * permission. 422 * 423 * @param device Paired bluetooth device 424 * @param priority 425 * @return true if priority is set, false on error 426 * @hide 427 */ setPriority(BluetoothDevice device, int priority)428 public boolean setPriority(BluetoothDevice device, int priority) { 429 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 430 try { 431 mServiceLock.readLock().lock(); 432 if (mService != null && isEnabled() 433 && isValidDevice(device)) { 434 if (priority != BluetoothProfile.PRIORITY_OFF && 435 priority != BluetoothProfile.PRIORITY_ON) { 436 return false; 437 } 438 return mService.setPriority(device, priority); 439 } 440 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 441 return false; 442 } catch (RemoteException e) { 443 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 444 return false; 445 } finally { 446 mServiceLock.readLock().unlock(); 447 } 448 } 449 450 /** 451 * Get the priority of the profile. 452 * 453 * <p> The priority can be any of: 454 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, 455 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} 456 * 457 * @param device Bluetooth device 458 * @return priority of the device 459 * @hide 460 */ 461 @RequiresPermission(Manifest.permission.BLUETOOTH) getPriority(BluetoothDevice device)462 public int getPriority(BluetoothDevice device) { 463 if (VDBG) log("getPriority(" + device + ")"); 464 try { 465 mServiceLock.readLock().lock(); 466 if (mService != null && isEnabled() 467 && isValidDevice(device)) { 468 return mService.getPriority(device); 469 } 470 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 471 return BluetoothProfile.PRIORITY_OFF; 472 } catch (RemoteException e) { 473 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 474 return BluetoothProfile.PRIORITY_OFF; 475 } finally { 476 mServiceLock.readLock().unlock(); 477 } 478 } 479 480 /** 481 * Checks if Avrcp device supports the absolute volume feature. 482 * 483 * @return true if device supports absolute volume 484 * @hide 485 */ isAvrcpAbsoluteVolumeSupported()486 public boolean isAvrcpAbsoluteVolumeSupported() { 487 if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); 488 try { 489 mServiceLock.readLock().lock(); 490 if (mService != null && isEnabled()) { 491 return mService.isAvrcpAbsoluteVolumeSupported(); 492 } 493 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 494 return false; 495 } catch (RemoteException e) { 496 Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); 497 return false; 498 } finally { 499 mServiceLock.readLock().unlock(); 500 } 501 } 502 503 /** 504 * Tells remote device to adjust volume. Only if absolute volume is 505 * supported. Uses the following values: 506 * <ul> 507 * <li>{@link AudioManager#ADJUST_LOWER}</li> 508 * <li>{@link AudioManager#ADJUST_RAISE}</li> 509 * <li>{@link AudioManager#ADJUST_MUTE}</li> 510 * <li>{@link AudioManager#ADJUST_UNMUTE}</li> 511 * </ul> 512 * 513 * @param direction One of the supported adjust values. 514 * @hide 515 */ adjustAvrcpAbsoluteVolume(int direction)516 public void adjustAvrcpAbsoluteVolume(int direction) { 517 if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume"); 518 try { 519 mServiceLock.readLock().lock(); 520 if (mService != null && isEnabled()) { 521 mService.adjustAvrcpAbsoluteVolume(direction); 522 } 523 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 524 } catch (RemoteException e) { 525 Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); 526 } finally { 527 mServiceLock.readLock().unlock(); 528 } 529 } 530 531 /** 532 * Tells remote device to set an absolute volume. Only if absolute volume is supported 533 * 534 * @param volume Absolute volume to be set on AVRCP side 535 * @hide 536 */ setAvrcpAbsoluteVolume(int volume)537 public void setAvrcpAbsoluteVolume(int volume) { 538 if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); 539 try { 540 mServiceLock.readLock().lock(); 541 if (mService != null && isEnabled()) { 542 mService.setAvrcpAbsoluteVolume(volume); 543 } 544 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 545 } catch (RemoteException e) { 546 Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); 547 } finally { 548 mServiceLock.readLock().unlock(); 549 } 550 } 551 552 /** 553 * Check if A2DP profile is streaming music. 554 * 555 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 556 * 557 * @param device BluetoothDevice device 558 */ isA2dpPlaying(BluetoothDevice device)559 public boolean isA2dpPlaying(BluetoothDevice device) { 560 try { 561 mServiceLock.readLock().lock(); 562 if (mService != null && isEnabled() 563 && isValidDevice(device)) { 564 return mService.isA2dpPlaying(device); 565 } 566 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 567 return false; 568 } catch (RemoteException e) { 569 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 570 return false; 571 } finally { 572 mServiceLock.readLock().unlock(); 573 } 574 } 575 576 /** 577 * This function checks if the remote device is an AVCRP 578 * target and thus whether we should send volume keys 579 * changes or not. 580 * @hide 581 */ shouldSendVolumeKeys(BluetoothDevice device)582 public boolean shouldSendVolumeKeys(BluetoothDevice device) { 583 if (isEnabled() && isValidDevice(device)) { 584 ParcelUuid[] uuids = device.getUuids(); 585 if (uuids == null) return false; 586 587 for (ParcelUuid uuid: uuids) { 588 if (BluetoothUuid.isAvrcpTarget(uuid)) { 589 return true; 590 } 591 } 592 } 593 return false; 594 } 595 596 /** 597 * Gets the current codec status (configuration and capability). 598 * 599 * @return the current codec status 600 * @hide 601 */ getCodecStatus()602 public BluetoothCodecStatus getCodecStatus() { 603 if (DBG) Log.d(TAG, "getCodecStatus"); 604 try { 605 mServiceLock.readLock().lock(); 606 if (mService != null && isEnabled()) { 607 return mService.getCodecStatus(); 608 } 609 if (mService == null) { 610 Log.w(TAG, "Proxy not attached to service"); 611 } 612 return null; 613 } catch (RemoteException e) { 614 Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); 615 return null; 616 } finally { 617 mServiceLock.readLock().unlock(); 618 } 619 } 620 621 /** 622 * Sets the codec configuration preference. 623 * 624 * @param codecConfig the codec configuration preference 625 * @hide 626 */ setCodecConfigPreference(BluetoothCodecConfig codecConfig)627 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { 628 if (DBG) Log.d(TAG, "setCodecConfigPreference"); 629 try { 630 mServiceLock.readLock().lock(); 631 if (mService != null && isEnabled()) { 632 mService.setCodecConfigPreference(codecConfig); 633 } 634 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 635 return; 636 } catch (RemoteException e) { 637 Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e); 638 return; 639 } finally { 640 mServiceLock.readLock().unlock(); 641 } 642 } 643 644 /** 645 * Enables the optional codecs. 646 * 647 * @hide 648 */ enableOptionalCodecs()649 public void enableOptionalCodecs() { 650 if (DBG) Log.d(TAG, "enableOptionalCodecs"); 651 enableDisableOptionalCodecs(true); 652 } 653 654 /** 655 * Disables the optional codecs. 656 * 657 * @hide 658 */ disableOptionalCodecs()659 public void disableOptionalCodecs() { 660 if (DBG) Log.d(TAG, "disableOptionalCodecs"); 661 enableDisableOptionalCodecs(false); 662 } 663 664 /** 665 * Enables or disables the optional codecs. 666 * 667 * @param enable if true, enable the optional codecs, other disable them 668 */ enableDisableOptionalCodecs(boolean enable)669 private void enableDisableOptionalCodecs(boolean enable) { 670 try { 671 mServiceLock.readLock().lock(); 672 if (mService != null && isEnabled()) { 673 if (enable) { 674 mService.enableOptionalCodecs(); 675 } else { 676 mService.disableOptionalCodecs(); 677 } 678 } 679 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 680 return; 681 } catch (RemoteException e) { 682 Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); 683 return; 684 } finally { 685 mServiceLock.readLock().unlock(); 686 } 687 } 688 689 /** 690 * Returns whether this device supports optional codecs. 691 * 692 * @param device The device to check 693 * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or 694 * OPTIONAL_CODECS_SUPPORTED. 695 * 696 * @hide 697 */ supportsOptionalCodecs(BluetoothDevice device)698 public int supportsOptionalCodecs(BluetoothDevice device) { 699 try { 700 mServiceLock.readLock().lock(); 701 if (mService != null && isEnabled() && isValidDevice(device)) { 702 return mService.supportsOptionalCodecs(device); 703 } 704 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 705 return OPTIONAL_CODECS_SUPPORT_UNKNOWN; 706 } catch (RemoteException e) { 707 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); 708 return OPTIONAL_CODECS_SUPPORT_UNKNOWN; 709 } finally { 710 mServiceLock.readLock().unlock(); 711 } 712 } 713 714 /** 715 * Returns whether this device should have optional codecs enabled. 716 * 717 * @param device The device in question. 718 * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or 719 * OPTIONAL_CODECS_PREF_DISABLED. 720 * 721 * @hide 722 */ getOptionalCodecsEnabled(BluetoothDevice device)723 public int getOptionalCodecsEnabled(BluetoothDevice device) { 724 try { 725 mServiceLock.readLock().lock(); 726 if (mService != null && isEnabled() && isValidDevice(device)) { 727 return mService.getOptionalCodecsEnabled(device); 728 } 729 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 730 return OPTIONAL_CODECS_PREF_UNKNOWN; 731 } catch (RemoteException e) { 732 Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); 733 return OPTIONAL_CODECS_PREF_UNKNOWN; 734 } finally { 735 mServiceLock.readLock().unlock(); 736 } 737 } 738 739 /** 740 * Sets a persistent preference for whether a given device should have optional codecs enabled. 741 * 742 * @param device The device to set this preference for. 743 * @param value Whether the optional codecs should be enabled for this device. This should be 744 * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or 745 * OPTIONAL_CODECS_PREF_DISABLED. 746 * @hide 747 */ setOptionalCodecsEnabled(BluetoothDevice device, int value)748 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { 749 try { 750 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && 751 value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED && 752 value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 753 Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); 754 return; 755 } 756 mServiceLock.readLock().lock(); 757 if (mService != null && isEnabled() 758 && isValidDevice(device)) { 759 mService.setOptionalCodecsEnabled(device, value); 760 } 761 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 762 return; 763 } catch (RemoteException e) { 764 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 765 return; 766 } finally { 767 mServiceLock.readLock().unlock(); 768 } 769 } 770 771 /** 772 * Helper for converting a state to a string. 773 * 774 * For debug use only - strings are not internationalized. 775 * @hide 776 */ stateToString(int state)777 public static String stateToString(int state) { 778 switch (state) { 779 case STATE_DISCONNECTED: 780 return "disconnected"; 781 case STATE_CONNECTING: 782 return "connecting"; 783 case STATE_CONNECTED: 784 return "connected"; 785 case STATE_DISCONNECTING: 786 return "disconnecting"; 787 case STATE_PLAYING: 788 return "playing"; 789 case STATE_NOT_PLAYING: 790 return "not playing"; 791 default: 792 return "<unknown state " + state + ">"; 793 } 794 } 795 796 private final ServiceConnection mConnection = new ServiceConnection() { 797 public void onServiceConnected(ComponentName className, IBinder service) { 798 if (DBG) Log.d(TAG, "Proxy object connected"); 799 try { 800 mServiceLock.writeLock().lock(); 801 mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); 802 } finally { 803 mServiceLock.writeLock().unlock(); 804 } 805 806 if (mServiceListener != null) { 807 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); 808 } 809 } 810 public void onServiceDisconnected(ComponentName className) { 811 if (DBG) Log.d(TAG, "Proxy object disconnected"); 812 try { 813 mServiceLock.writeLock().lock(); 814 mService = null; 815 } finally { 816 mServiceLock.writeLock().unlock(); 817 } 818 if (mServiceListener != null) { 819 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); 820 } 821 } 822 }; 823 isEnabled()824 private boolean isEnabled() { 825 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; 826 return false; 827 } 828 isValidDevice(BluetoothDevice device)829 private boolean isValidDevice(BluetoothDevice device) { 830 if (device == null) return false; 831 832 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; 833 return false; 834 } 835 log(String msg)836 private static void log(String msg) { 837 Log.d(TAG, msg); 838 } 839 } 840