blob: 8f8083ed73e2c22ea3f5d2512e883661c8fd6996 [file] [log] [blame]
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -08001/*
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
17package android.bluetooth;
18
19import android.Manifest;
Hansong Zhangd7b35912018-03-16 09:15:48 -070020import android.annotation.Nullable;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080021import android.annotation.RequiresPermission;
22import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.ServiceConnection;
28import android.os.Binder;
29import android.os.IBinder;
30import android.os.RemoteException;
31import android.util.Log;
32
33import com.android.internal.annotations.GuardedBy;
34
35import java.util.ArrayList;
36import java.util.List;
37import 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 */
50public 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 */
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
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
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
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 */
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 */
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
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
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
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 /**
Hansong Zhangd7b35912018-03-16 09:15:48 -0700383 * 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 */
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 * Check whether the device is active.
425 *
426 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
427 * permission.
428 *
429 * @return the connected device that is active or null if no device
430 * is active
431 * @hide
432 */
433 @RequiresPermission(Manifest.permission.BLUETOOTH)
434 public boolean isActiveDevice(@Nullable BluetoothDevice device) {
435 if (VDBG) log("isActiveDevice()");
436 try {
437 mServiceLock.readLock().lock();
438 if (mService != null && isEnabled()
439 && ((device == null) || isValidDevice(device))) {
440 return mService.isActiveDevice(device);
441 }
442 if (mService == null) Log.w(TAG, "Proxy not attached to service");
443 return false;
444 } catch (RemoteException e) {
445 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
446 return false;
447 } finally {
448 mServiceLock.readLock().unlock();
449 }
450 }
451
452 /**
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800453 * 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 */
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)
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 */
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)
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)
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 */
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)
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)
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)
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
749 private boolean isEnabled() {
750 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
751 return false;
752 }
753
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
761 private static void log(String msg) {
762 Log.d(TAG, msg);
763 }
764}