1 /* 2 * Copyright 2018 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.Nullable; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SdkConstant; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.ServiceConnection; 28 import android.os.Binder; 29 import android.os.IBinder; 30 import android.os.RemoteException; 31 import android.util.Log; 32 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.concurrent.locks.ReentrantReadWriteLock; 38 39 /** 40 * This class provides the public APIs to control the Bluetooth Hearing Aid 41 * profile. 42 * 43 * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid 44 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 45 * the BluetoothHearingAid proxy object. 46 * 47 * <p> Each method is protected with its appropriate permission. 48 * @hide 49 */ 50 public final class BluetoothHearingAid implements BluetoothProfile { 51 private static final String TAG = "BluetoothHearingAid"; 52 private static final boolean DBG = false; 53 private static final boolean VDBG = false; 54 55 /** 56 * Intent used to broadcast the change in connection state of the Hearing Aid 57 * profile. 58 * 59 * <p>This intent will have 3 extras: 60 * <ul> 61 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 62 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> 63 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 64 * </ul> 65 * 66 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 67 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 68 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 69 * 70 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 71 * receive. 72 */ 73 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 74 public static final String ACTION_CONNECTION_STATE_CHANGED = 75 "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED"; 76 77 /** 78 * Intent used to broadcast the change in the Playing state of the Hearing Aid 79 * profile. 80 * 81 * <p>This intent will have 3 extras: 82 * <ul> 83 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 84 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> 85 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 86 * </ul> 87 * 88 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 89 * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, 90 * 91 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 92 * receive. 93 */ 94 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 95 public static final String ACTION_PLAYING_STATE_CHANGED = 96 "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED"; 97 98 /** 99 * Intent used to broadcast the selection of a connected device as active. 100 * 101 * <p>This intent will have one extra: 102 * <ul> 103 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can 104 * be null if no device is active. </li> 105 * </ul> 106 * 107 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 108 * receive. 109 */ 110 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 111 public static final String ACTION_ACTIVE_DEVICE_CHANGED = 112 "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; 113 114 /** 115 * Hearing Aid device is streaming music. This state can be one of 116 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of 117 * {@link #ACTION_PLAYING_STATE_CHANGED} intent. 118 */ 119 public static final int STATE_PLAYING = 10; 120 121 /** 122 * Hearing Aid device is NOT streaming music. This state can be one of 123 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of 124 * {@link #ACTION_PLAYING_STATE_CHANGED} intent. 125 */ 126 public static final int STATE_NOT_PLAYING = 11; 127 128 /** This device represents Left Hearing Aid. */ 129 public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT; 130 131 /** This device represents Right Hearing Aid. */ 132 public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT; 133 134 /** This device is Monaural. */ 135 public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL; 136 137 /** This device is Binaural (should receive only left or right audio). */ 138 public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL; 139 140 /** Can't read ClientID for this device */ 141 public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; 142 143 private Context mContext; 144 private ServiceListener mServiceListener; 145 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); 146 @GuardedBy("mServiceLock") 147 private IBluetoothHearingAid mService; 148 private BluetoothAdapter mAdapter; 149 150 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 151 new IBluetoothStateChangeCallback.Stub() { 152 public void onBluetoothStateChange(boolean up) { 153 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); 154 if (!up) { 155 if (VDBG) Log.d(TAG, "Unbinding service..."); 156 try { 157 mServiceLock.writeLock().lock(); 158 mService = null; 159 mContext.unbindService(mConnection); 160 } catch (Exception re) { 161 Log.e(TAG, "", re); 162 } finally { 163 mServiceLock.writeLock().unlock(); 164 } 165 } else { 166 try { 167 mServiceLock.readLock().lock(); 168 if (mService == null) { 169 if (VDBG) Log.d(TAG, "Binding service..."); 170 doBind(); 171 } 172 } catch (Exception re) { 173 Log.e(TAG, "", re); 174 } finally { 175 mServiceLock.readLock().unlock(); 176 } 177 } 178 } 179 }; 180 181 /** 182 * Create a BluetoothHearingAid proxy object for interacting with the local 183 * Bluetooth Hearing Aid service. 184 */ BluetoothHearingAid(Context context, ServiceListener l)185 /*package*/ BluetoothHearingAid(Context context, ServiceListener l) { 186 mContext = context; 187 mServiceListener = l; 188 mAdapter = BluetoothAdapter.getDefaultAdapter(); 189 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 190 if (mgr != null) { 191 try { 192 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 193 } catch (RemoteException e) { 194 Log.e(TAG, "", e); 195 } 196 } 197 198 doBind(); 199 } 200 doBind()201 void doBind() { 202 Intent intent = new Intent(IBluetoothHearingAid.class.getName()); 203 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 204 intent.setComponent(comp); 205 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, 206 android.os.Process.myUserHandle())) { 207 Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent); 208 return; 209 } 210 } 211 close()212 /*package*/ void close() { 213 mServiceListener = null; 214 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 215 if (mgr != null) { 216 try { 217 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 218 } catch (Exception e) { 219 Log.e(TAG, "", e); 220 } 221 } 222 223 try { 224 mServiceLock.writeLock().lock(); 225 if (mService != null) { 226 mService = null; 227 mContext.unbindService(mConnection); 228 } 229 } catch (Exception re) { 230 Log.e(TAG, "", re); 231 } finally { 232 mServiceLock.writeLock().unlock(); 233 } 234 } 235 236 @Override finalize()237 public void finalize() { 238 // The empty finalize needs to be kept or the 239 // cts signature tests would fail. 240 } 241 242 /** 243 * Initiate connection to a profile of the remote bluetooth device. 244 * 245 * <p> This API returns false in scenarios like the profile on the 246 * device is already connected or Bluetooth is not turned on. 247 * When this API returns true, it is guaranteed that 248 * connection state intent for the profile will be broadcasted with 249 * the state. Users can get the connection state of the profile 250 * from this intent. 251 * 252 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 253 * permission. 254 * 255 * @param device Remote Bluetooth Device 256 * @return false on immediate error, true otherwise 257 * @hide 258 */ connect(BluetoothDevice device)259 public boolean connect(BluetoothDevice device) { 260 if (DBG) log("connect(" + device + ")"); 261 try { 262 mServiceLock.readLock().lock(); 263 if (mService != null && isEnabled() && isValidDevice(device)) { 264 return mService.connect(device); 265 } 266 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 267 return false; 268 } catch (RemoteException e) { 269 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 270 return false; 271 } finally { 272 mServiceLock.readLock().unlock(); 273 } 274 } 275 276 /** 277 * Initiate disconnection from a profile 278 * 279 * <p> This API will return false in scenarios like the profile on the 280 * Bluetooth device is not in connected state etc. When this API returns, 281 * true, it is guaranteed that the connection state change 282 * intent will be broadcasted with the state. Users can get the 283 * disconnection state of the profile from this intent. 284 * 285 * <p> If the disconnection is initiated by a remote device, the state 286 * will transition from {@link #STATE_CONNECTED} to 287 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the 288 * host (local) device the state will transition from 289 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to 290 * state {@link #STATE_DISCONNECTED}. The transition to 291 * {@link #STATE_DISCONNECTING} can be used to distinguish between the 292 * two scenarios. 293 * 294 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 295 * permission. 296 * 297 * @param device Remote Bluetooth Device 298 * @return false on immediate error, true otherwise 299 * @hide 300 */ disconnect(BluetoothDevice device)301 public boolean disconnect(BluetoothDevice device) { 302 if (DBG) log("disconnect(" + device + ")"); 303 try { 304 mServiceLock.readLock().lock(); 305 if (mService != null && isEnabled() && isValidDevice(device)) { 306 return mService.disconnect(device); 307 } 308 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 309 return false; 310 } catch (RemoteException e) { 311 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 312 return false; 313 } finally { 314 mServiceLock.readLock().unlock(); 315 } 316 } 317 318 /** 319 * {@inheritDoc} 320 */ 321 @Override getConnectedDevices()322 public List<BluetoothDevice> getConnectedDevices() { 323 if (VDBG) log("getConnectedDevices()"); 324 try { 325 mServiceLock.readLock().lock(); 326 if (mService != null && isEnabled()) { 327 return mService.getConnectedDevices(); 328 } 329 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 330 return new ArrayList<BluetoothDevice>(); 331 } catch (RemoteException e) { 332 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 333 return new ArrayList<BluetoothDevice>(); 334 } finally { 335 mServiceLock.readLock().unlock(); 336 } 337 } 338 339 /** 340 * {@inheritDoc} 341 */ 342 @Override getDevicesMatchingConnectionStates(int[] states)343 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 344 if (VDBG) log("getDevicesMatchingStates()"); 345 try { 346 mServiceLock.readLock().lock(); 347 if (mService != null && isEnabled()) { 348 return mService.getDevicesMatchingConnectionStates(states); 349 } 350 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 351 return new ArrayList<BluetoothDevice>(); 352 } catch (RemoteException e) { 353 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 354 return new ArrayList<BluetoothDevice>(); 355 } finally { 356 mServiceLock.readLock().unlock(); 357 } 358 } 359 360 /** 361 * {@inheritDoc} 362 */ 363 @Override getConnectionState(BluetoothDevice device)364 public int getConnectionState(BluetoothDevice device) { 365 if (VDBG) log("getState(" + device + ")"); 366 try { 367 mServiceLock.readLock().lock(); 368 if (mService != null && isEnabled() 369 && isValidDevice(device)) { 370 return mService.getConnectionState(device); 371 } 372 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 373 return BluetoothProfile.STATE_DISCONNECTED; 374 } catch (RemoteException e) { 375 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 376 return BluetoothProfile.STATE_DISCONNECTED; 377 } finally { 378 mServiceLock.readLock().unlock(); 379 } 380 } 381 382 /** 383 * Select a connected device as active. 384 * 385 * The active device selection is per profile. An active device's 386 * purpose is profile-specific. For example, Hearing Aid audio 387 * streaming is to the active Hearing Aid device. If a remote device 388 * is not connected, it cannot be selected as active. 389 * 390 * <p> This API returns false in scenarios like the profile on the 391 * device is not connected or Bluetooth is not turned on. 392 * When this API returns true, it is guaranteed that the 393 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted 394 * with the active device. 395 * 396 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 397 * permission. 398 * 399 * @param device the remote Bluetooth device. Could be null to clear 400 * the active device and stop streaming audio to a Bluetooth device. 401 * @return false on immediate error, true otherwise 402 * @hide 403 */ setActiveDevice(@ullable BluetoothDevice device)404 public boolean setActiveDevice(@Nullable BluetoothDevice device) { 405 if (DBG) log("setActiveDevice(" + device + ")"); 406 try { 407 mServiceLock.readLock().lock(); 408 if (mService != null && isEnabled() 409 && ((device == null) || isValidDevice(device))) { 410 mService.setActiveDevice(device); 411 return true; 412 } 413 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 414 return false; 415 } catch (RemoteException e) { 416 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 417 return false; 418 } finally { 419 mServiceLock.readLock().unlock(); 420 } 421 } 422 423 /** 424 * Get the connected physical Hearing Aid devices that are active 425 * 426 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} 427 * permission. 428 * 429 * @return the list of active devices. The first element is the left active 430 * device; the second element is the right active device. If either or both side 431 * is not active, it will be null on that position. Returns empty list on error. 432 * @hide 433 */ 434 @RequiresPermission(Manifest.permission.BLUETOOTH) getActiveDevices()435 public List<BluetoothDevice> getActiveDevices() { 436 if (VDBG) log("getActiveDevices()"); 437 try { 438 mServiceLock.readLock().lock(); 439 if (mService != null && isEnabled()) { 440 return mService.getActiveDevices(); 441 } 442 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 443 return new ArrayList<>(); 444 } catch (RemoteException e) { 445 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 446 return new ArrayList<>(); 447 } finally { 448 mServiceLock.readLock().unlock(); 449 } 450 } 451 452 /** 453 * Set priority of the profile 454 * 455 * <p> The device should already be paired. 456 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager 457 * {@link #PRIORITY_OFF}, 458 * 459 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 460 * permission. 461 * 462 * @param device Paired bluetooth device 463 * @param priority 464 * @return true if priority is set, false on error 465 * @hide 466 */ setPriority(BluetoothDevice device, int priority)467 public boolean setPriority(BluetoothDevice device, int priority) { 468 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 469 try { 470 mServiceLock.readLock().lock(); 471 if (mService != null && isEnabled() 472 && isValidDevice(device)) { 473 if (priority != BluetoothProfile.PRIORITY_OFF 474 && priority != BluetoothProfile.PRIORITY_ON) { 475 return false; 476 } 477 return mService.setPriority(device, priority); 478 } 479 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 480 return false; 481 } catch (RemoteException e) { 482 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 483 return false; 484 } finally { 485 mServiceLock.readLock().unlock(); 486 } 487 } 488 489 /** 490 * Get the priority of the profile. 491 * 492 * <p> The priority can be any of: 493 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, 494 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} 495 * 496 * @param device Bluetooth device 497 * @return priority of the device 498 * @hide 499 */ 500 @RequiresPermission(Manifest.permission.BLUETOOTH) getPriority(BluetoothDevice device)501 public int getPriority(BluetoothDevice device) { 502 if (VDBG) log("getPriority(" + device + ")"); 503 try { 504 mServiceLock.readLock().lock(); 505 if (mService != null && isEnabled() 506 && isValidDevice(device)) { 507 return mService.getPriority(device); 508 } 509 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 510 return BluetoothProfile.PRIORITY_OFF; 511 } catch (RemoteException e) { 512 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 513 return BluetoothProfile.PRIORITY_OFF; 514 } finally { 515 mServiceLock.readLock().unlock(); 516 } 517 } 518 519 /** 520 * Helper for converting a state to a string. 521 * 522 * For debug use only - strings are not internationalized. 523 * 524 * @hide 525 */ stateToString(int state)526 public static String stateToString(int state) { 527 switch (state) { 528 case STATE_DISCONNECTED: 529 return "disconnected"; 530 case STATE_CONNECTING: 531 return "connecting"; 532 case STATE_CONNECTED: 533 return "connected"; 534 case STATE_DISCONNECTING: 535 return "disconnecting"; 536 case STATE_PLAYING: 537 return "playing"; 538 case STATE_NOT_PLAYING: 539 return "not playing"; 540 default: 541 return "<unknown state " + state + ">"; 542 } 543 } 544 545 /** 546 * Get the volume of the device. 547 * 548 * <p> The volume is between -128 dB (mute) to 0 dB. 549 * 550 * @return volume of the hearing aid device. 551 * @hide 552 */ 553 @RequiresPermission(Manifest.permission.BLUETOOTH) getVolume()554 public int getVolume() { 555 if (VDBG) { 556 log("getVolume()"); 557 } 558 try { 559 mServiceLock.readLock().lock(); 560 if (mService != null && isEnabled()) { 561 return mService.getVolume(); 562 } 563 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 564 return 0; 565 } catch (RemoteException e) { 566 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 567 return 0; 568 } finally { 569 mServiceLock.readLock().unlock(); 570 } 571 } 572 573 /** 574 * Tells remote device to adjust volume. Uses the following values: 575 * <ul> 576 * <li>{@link AudioManager#ADJUST_LOWER}</li> 577 * <li>{@link AudioManager#ADJUST_RAISE}</li> 578 * <li>{@link AudioManager#ADJUST_MUTE}</li> 579 * <li>{@link AudioManager#ADJUST_UNMUTE}</li> 580 * </ul> 581 * 582 * @param direction One of the supported adjust values. 583 * @hide 584 */ 585 @RequiresPermission(Manifest.permission.BLUETOOTH) adjustVolume(int direction)586 public void adjustVolume(int direction) { 587 if (DBG) log("adjustVolume(" + direction + ")"); 588 589 try { 590 mServiceLock.readLock().lock(); 591 592 if (mService == null) { 593 Log.w(TAG, "Proxy not attached to service"); 594 return; 595 } 596 597 if (!isEnabled()) return; 598 599 mService.adjustVolume(direction); 600 } catch (RemoteException e) { 601 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 602 } finally { 603 mServiceLock.readLock().unlock(); 604 } 605 } 606 607 /** 608 * Tells remote device to set an absolute volume. 609 * 610 * @param volume Absolute volume to be set on remote 611 * @hide 612 */ setVolume(int volume)613 public void setVolume(int volume) { 614 if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); 615 616 try { 617 mServiceLock.readLock().lock(); 618 if (mService == null) { 619 Log.w(TAG, "Proxy not attached to service"); 620 return; 621 } 622 623 if (!isEnabled()) return; 624 625 mService.setVolume(volume); 626 } catch (RemoteException e) { 627 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 628 } finally { 629 mServiceLock.readLock().unlock(); 630 } 631 } 632 633 /** 634 * Get the CustomerId of the device. 635 * 636 * @param device Bluetooth device 637 * @return the CustomerId of the device 638 * @hide 639 */ 640 @RequiresPermission(Manifest.permission.BLUETOOTH) getHiSyncId(BluetoothDevice device)641 public long getHiSyncId(BluetoothDevice device) { 642 if (VDBG) { 643 log("getCustomerId(" + device + ")"); 644 } 645 try { 646 mServiceLock.readLock().lock(); 647 if (mService == null) { 648 Log.w(TAG, "Proxy not attached to service"); 649 return HI_SYNC_ID_INVALID; 650 } 651 652 if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; 653 654 return mService.getHiSyncId(device); 655 } catch (RemoteException e) { 656 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 657 return HI_SYNC_ID_INVALID; 658 } finally { 659 mServiceLock.readLock().unlock(); 660 } 661 } 662 663 /** 664 * Get the side of the device. 665 * 666 * @param device Bluetooth device. 667 * @return SIDE_LEFT or SIDE_RIGHT 668 * @hide 669 */ 670 @RequiresPermission(Manifest.permission.BLUETOOTH) getDeviceSide(BluetoothDevice device)671 public int getDeviceSide(BluetoothDevice device) { 672 if (VDBG) { 673 log("getDeviceSide(" + device + ")"); 674 } 675 try { 676 mServiceLock.readLock().lock(); 677 if (mService != null && isEnabled() 678 && isValidDevice(device)) { 679 return mService.getDeviceSide(device); 680 } 681 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 682 return SIDE_LEFT; 683 } catch (RemoteException e) { 684 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 685 return SIDE_LEFT; 686 } finally { 687 mServiceLock.readLock().unlock(); 688 } 689 } 690 691 /** 692 * Get the mode of the device. 693 * 694 * @param device Bluetooth device 695 * @return MODE_MONAURAL or MODE_BINAURAL 696 * @hide 697 */ 698 @RequiresPermission(Manifest.permission.BLUETOOTH) getDeviceMode(BluetoothDevice device)699 public int getDeviceMode(BluetoothDevice device) { 700 if (VDBG) { 701 log("getDeviceMode(" + device + ")"); 702 } 703 try { 704 mServiceLock.readLock().lock(); 705 if (mService != null && isEnabled() 706 && isValidDevice(device)) { 707 return mService.getDeviceMode(device); 708 } 709 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 710 return MODE_MONAURAL; 711 } catch (RemoteException e) { 712 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 713 return MODE_MONAURAL; 714 } finally { 715 mServiceLock.readLock().unlock(); 716 } 717 } 718 719 private final ServiceConnection mConnection = new ServiceConnection() { 720 public void onServiceConnected(ComponentName className, IBinder service) { 721 if (DBG) Log.d(TAG, "Proxy object connected"); 722 try { 723 mServiceLock.writeLock().lock(); 724 mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); 725 } finally { 726 mServiceLock.writeLock().unlock(); 727 } 728 729 if (mServiceListener != null) { 730 mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, 731 BluetoothHearingAid.this); 732 } 733 } 734 735 public void onServiceDisconnected(ComponentName className) { 736 if (DBG) Log.d(TAG, "Proxy object disconnected"); 737 try { 738 mServiceLock.writeLock().lock(); 739 mService = null; 740 } finally { 741 mServiceLock.writeLock().unlock(); 742 } 743 if (mServiceListener != null) { 744 mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID); 745 } 746 } 747 }; 748 isEnabled()749 private boolean isEnabled() { 750 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; 751 return false; 752 } 753 isValidDevice(BluetoothDevice device)754 private boolean isValidDevice(BluetoothDevice device) { 755 if (device == null) return false; 756 757 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; 758 return false; 759 } 760 log(String msg)761 private static void log(String msg) { 762 Log.d(TAG, msg); 763 } 764 } 765