blob: a812c32ae8685fe945cb82e6b5445759fbb838b0 [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;
Stanley Tng9d376672019-02-28 12:22:45 -080020import android.annotation.NonNull;
Hansong Zhang7ca303c2018-03-16 09:15:48 -070021import android.annotation.Nullable;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080022import android.annotation.RequiresPermission;
23import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
Mathew Inwood3a75f262019-06-27 12:47:38 +010025import android.annotation.UnsupportedAppUsage;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080026import android.content.Context;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080027import android.os.Binder;
28import android.os.IBinder;
29import android.os.RemoteException;
30import android.util.Log;
31
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080032import java.util.ArrayList;
33import java.util.List;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080034
35/**
Stanley Tng1f5ea662018-11-15 17:11:36 -080036 * This class provides the public APIs to control the Hearing Aid profile.
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080037 *
38 * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
39 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
40 * the BluetoothHearingAid proxy object.
41 *
Stanley Tng1f5ea662018-11-15 17:11:36 -080042 * <p> Android only supports one set of connected Bluetooth Hearing Aid device at a time. Each
43 * method is protected with its appropriate permission.
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080044 */
45public final class BluetoothHearingAid implements BluetoothProfile {
46 private static final String TAG = "BluetoothHearingAid";
Stanley Tng420a0eb2018-11-15 10:22:07 -080047 private static final boolean DBG = true;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080048 private static final boolean VDBG = false;
49
50 /**
51 * Intent used to broadcast the change in connection state of the Hearing Aid
Stanley Tng1f5ea662018-11-15 17:11:36 -080052 * profile. Please note that in the binaural case, there will be two different LE devices for
53 * the left and right side and each device will have their own connection state changes.S
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080054 *
55 * <p>This intent will have 3 extras:
56 * <ul>
57 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
58 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
59 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
60 * </ul>
61 *
62 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
63 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
64 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
65 *
66 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
67 * receive.
68 */
69 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
70 public static final String ACTION_CONNECTION_STATE_CHANGED =
71 "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
72
73 /**
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080074 * Intent used to broadcast the selection of a connected device as active.
75 *
76 * <p>This intent will have one extra:
77 * <ul>
78 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
79 * be null if no device is active. </li>
80 * </ul>
81 *
82 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
83 * receive.
Stanley Tng1f5ea662018-11-15 17:11:36 -080084 *
85 * @hide
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080086 */
Mathew Inwood3a75f262019-06-27 12:47:38 +010087 @UnsupportedAppUsage
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080088 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
89 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
90 "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
91
92 /**
Stanley Tng1f5ea662018-11-15 17:11:36 -080093 * This device represents Left Hearing Aid.
94 *
95 * @hide
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080096 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080097 public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
98
Stanley Tng1f5ea662018-11-15 17:11:36 -080099 /**
100 * This device represents Right Hearing Aid.
101 *
102 * @hide
103 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800104 public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
105
Stanley Tng1f5ea662018-11-15 17:11:36 -0800106 /**
107 * This device is Monaural.
108 *
109 * @hide
110 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800111 public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
112
Stanley Tng1f5ea662018-11-15 17:11:36 -0800113 /**
114 * This device is Binaural (should receive only left or right audio).
115 *
116 * @hide
117 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800118 public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
119
Stanley Tng1f5ea662018-11-15 17:11:36 -0800120 /**
121 * Indicates the HiSyncID could not be read and is unavailable.
122 *
123 * @hide
124 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800125 public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
126
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800127 private BluetoothAdapter mAdapter;
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800128 private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector =
129 new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID,
130 "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
131 @Override
132 public IBluetoothHearingAid getServiceInterface(IBinder service) {
133 return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800134 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800135 };
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800136
137 /**
138 * Create a BluetoothHearingAid proxy object for interacting with the local
139 * Bluetooth Hearing Aid service.
140 */
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800141 /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800142 mAdapter = BluetoothAdapter.getDefaultAdapter();
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800143 mProfileConnector.connect(context, listener);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800144 }
145
146 /*package*/ void close() {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800147 mProfileConnector.disconnect();
148 }
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800149
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800150 private IBluetoothHearingAid getService() {
151 return mProfileConnector.getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800152 }
153
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800154 /**
155 * Initiate connection to a profile of the remote bluetooth device.
156 *
157 * <p> This API returns false in scenarios like the profile on the
158 * device is already connected or Bluetooth is not turned on.
159 * When this API returns true, it is guaranteed that
160 * connection state intent for the profile will be broadcasted with
161 * the state. Users can get the connection state of the profile
162 * from this intent.
163 *
164 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
165 * permission.
166 *
167 * @param device Remote Bluetooth Device
168 * @return false on immediate error, true otherwise
169 * @hide
170 */
171 public boolean connect(BluetoothDevice device) {
172 if (DBG) log("connect(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800173 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800174 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800175 if (service != null && isEnabled() && isValidDevice(device)) {
176 return service.connect(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800177 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800178 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800179 return false;
180 } catch (RemoteException e) {
181 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
182 return false;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800183 }
184 }
185
186 /**
187 * Initiate disconnection from a profile
188 *
189 * <p> This API will return false in scenarios like the profile on the
190 * Bluetooth device is not in connected state etc. When this API returns,
191 * true, it is guaranteed that the connection state change
192 * intent will be broadcasted with the state. Users can get the
193 * disconnection state of the profile from this intent.
194 *
195 * <p> If the disconnection is initiated by a remote device, the state
196 * will transition from {@link #STATE_CONNECTED} to
197 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
198 * host (local) device the state will transition from
199 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
200 * state {@link #STATE_DISCONNECTED}. The transition to
201 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
202 * two scenarios.
203 *
204 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
205 * permission.
206 *
207 * @param device Remote Bluetooth Device
208 * @return false on immediate error, true otherwise
209 * @hide
210 */
211 public boolean disconnect(BluetoothDevice device) {
212 if (DBG) log("disconnect(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800213 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800214 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800215 if (service != null && isEnabled() && isValidDevice(device)) {
216 return service.disconnect(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800217 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800218 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800219 return false;
220 } catch (RemoteException e) {
221 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
222 return false;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800223 }
224 }
225
226 /**
227 * {@inheritDoc}
228 */
229 @Override
Stanley Tng9d376672019-02-28 12:22:45 -0800230 public @NonNull List<BluetoothDevice> getConnectedDevices() {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800231 if (VDBG) log("getConnectedDevices()");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800232 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800233 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800234 if (service != null && isEnabled()) {
235 return service.getConnectedDevices();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800236 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800237 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800238 return new ArrayList<BluetoothDevice>();
239 } catch (RemoteException e) {
240 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
241 return new ArrayList<BluetoothDevice>();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800242 }
243 }
244
245 /**
246 * {@inheritDoc}
247 */
Stanley Tng7f3b7812019-03-14 16:18:29 -0700248 @Override
249 public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
Stanley Tng9d376672019-02-28 12:22:45 -0800250 @NonNull int[] states) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800251 if (VDBG) log("getDevicesMatchingStates()");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800252 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800253 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800254 if (service != null && isEnabled()) {
255 return service.getDevicesMatchingConnectionStates(states);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800256 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800257 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800258 return new ArrayList<BluetoothDevice>();
259 } catch (RemoteException e) {
260 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
261 return new ArrayList<BluetoothDevice>();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800262 }
263 }
264
265 /**
266 * {@inheritDoc}
267 */
268 @Override
Stanley Tng7f3b7812019-03-14 16:18:29 -0700269 public @BluetoothProfile.BtProfileState int getConnectionState(
270 @NonNull BluetoothDevice device) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800271 if (VDBG) log("getState(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800272 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800273 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800274 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800275 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800276 return service.getConnectionState(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800277 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800278 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800279 return BluetoothProfile.STATE_DISCONNECTED;
280 } catch (RemoteException e) {
281 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
282 return BluetoothProfile.STATE_DISCONNECTED;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800283 }
284 }
285
286 /**
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700287 * Select a connected device as active.
288 *
289 * The active device selection is per profile. An active device's
290 * purpose is profile-specific. For example, Hearing Aid audio
291 * streaming is to the active Hearing Aid device. If a remote device
292 * is not connected, it cannot be selected as active.
293 *
294 * <p> This API returns false in scenarios like the profile on the
295 * device is not connected or Bluetooth is not turned on.
296 * When this API returns true, it is guaranteed that the
297 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
298 * with the active device.
299 *
300 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
301 * permission.
302 *
303 * @param device the remote Bluetooth device. Could be null to clear
304 * the active device and stop streaming audio to a Bluetooth device.
305 * @return false on immediate error, true otherwise
306 * @hide
307 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100308 @UnsupportedAppUsage
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700309 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
310 if (DBG) log("setActiveDevice(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800311 final IBluetoothHearingAid service = getService();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700312 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800313 if (service != null && isEnabled()
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700314 && ((device == null) || isValidDevice(device))) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800315 service.setActiveDevice(device);
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700316 return true;
317 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800318 if (service == null) Log.w(TAG, "Proxy not attached to service");
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700319 return false;
320 } catch (RemoteException e) {
321 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
322 return false;
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700323 }
324 }
325
326 /**
Hansong Zhang8d799f82018-03-28 16:53:10 -0700327 * Get the connected physical Hearing Aid devices that are active
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700328 *
329 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
330 * permission.
331 *
Hansong Zhang8d799f82018-03-28 16:53:10 -0700332 * @return the list of active devices. The first element is the left active
333 * device; the second element is the right active device. If either or both side
334 * is not active, it will be null on that position. Returns empty list on error.
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700335 * @hide
336 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100337 @UnsupportedAppUsage
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700338 @RequiresPermission(Manifest.permission.BLUETOOTH)
Hansong Zhang8d799f82018-03-28 16:53:10 -0700339 public List<BluetoothDevice> getActiveDevices() {
340 if (VDBG) log("getActiveDevices()");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800341 final IBluetoothHearingAid service = getService();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700342 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800343 if (service != null && isEnabled()) {
344 return service.getActiveDevices();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700345 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800346 if (service == null) Log.w(TAG, "Proxy not attached to service");
Hansong Zhang8d799f82018-03-28 16:53:10 -0700347 return new ArrayList<>();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700348 } catch (RemoteException e) {
349 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Hansong Zhang8d799f82018-03-28 16:53:10 -0700350 return new ArrayList<>();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700351 }
352 }
353
354 /**
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800355 * Set priority of the profile
356 *
357 * <p> The device should already be paired.
358 * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
359 * {@link #PRIORITY_OFF},
360 *
361 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
362 * permission.
363 *
364 * @param device Paired bluetooth device
365 * @param priority
366 * @return true if priority is set, false on error
367 * @hide
368 */
369 public boolean setPriority(BluetoothDevice device, int priority) {
370 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800371 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800372 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800373 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800374 && isValidDevice(device)) {
375 if (priority != BluetoothProfile.PRIORITY_OFF
376 && priority != BluetoothProfile.PRIORITY_ON) {
377 return false;
378 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800379 return service.setPriority(device, priority);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800380 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800381 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800382 return false;
383 } catch (RemoteException e) {
384 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
385 return false;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800386 }
387 }
388
389 /**
390 * Get the priority of the profile.
391 *
392 * <p> The priority can be any of:
393 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
394 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
395 *
396 * @param device Bluetooth device
397 * @return priority of the device
398 * @hide
399 */
400 @RequiresPermission(Manifest.permission.BLUETOOTH)
401 public int getPriority(BluetoothDevice device) {
402 if (VDBG) log("getPriority(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800403 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800404 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800405 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800406 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800407 return service.getPriority(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800408 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800409 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800410 return BluetoothProfile.PRIORITY_OFF;
411 } catch (RemoteException e) {
412 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
413 return BluetoothProfile.PRIORITY_OFF;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800414 }
415 }
416
417 /**
418 * Helper for converting a state to a string.
419 *
420 * For debug use only - strings are not internationalized.
421 *
422 * @hide
423 */
424 public static String stateToString(int state) {
425 switch (state) {
426 case STATE_DISCONNECTED:
427 return "disconnected";
428 case STATE_CONNECTING:
429 return "connecting";
430 case STATE_CONNECTED:
431 return "connected";
432 case STATE_DISCONNECTING:
433 return "disconnecting";
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800434 default:
435 return "<unknown state " + state + ">";
436 }
437 }
438
439 /**
440 * Get the volume of the device.
441 *
442 * <p> The volume is between -128 dB (mute) to 0 dB.
443 *
444 * @return volume of the hearing aid device.
445 * @hide
446 */
447 @RequiresPermission(Manifest.permission.BLUETOOTH)
448 public int getVolume() {
449 if (VDBG) {
450 log("getVolume()");
451 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800452 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800453 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800454 if (service != null && isEnabled()) {
455 return service.getVolume();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800456 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800457 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800458 return 0;
459 } catch (RemoteException e) {
460 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
461 return 0;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800462 }
463 }
464
465 /**
466 * Tells remote device to adjust volume. Uses the following values:
467 * <ul>
468 * <li>{@link AudioManager#ADJUST_LOWER}</li>
469 * <li>{@link AudioManager#ADJUST_RAISE}</li>
470 * <li>{@link AudioManager#ADJUST_MUTE}</li>
471 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
472 * </ul>
473 *
474 * @param direction One of the supported adjust values.
475 * @hide
476 */
477 @RequiresPermission(Manifest.permission.BLUETOOTH)
478 public void adjustVolume(int direction) {
479 if (DBG) log("adjustVolume(" + direction + ")");
480
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800481 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800482 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800483 if (service == null) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800484 Log.w(TAG, "Proxy not attached to service");
485 return;
486 }
487
488 if (!isEnabled()) return;
489
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800490 service.adjustVolume(direction);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800491 } catch (RemoteException e) {
492 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800493 }
494 }
495
496 /**
497 * Tells remote device to set an absolute volume.
498 *
499 * @param volume Absolute volume to be set on remote
500 * @hide
501 */
502 public void setVolume(int volume) {
503 if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
504
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800505 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800506 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800507 if (service == null) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800508 Log.w(TAG, "Proxy not attached to service");
509 return;
510 }
511
512 if (!isEnabled()) return;
513
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800514 service.setVolume(volume);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800515 } catch (RemoteException e) {
516 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800517 }
518 }
519
520 /**
521 * Get the CustomerId of the device.
522 *
523 * @param device Bluetooth device
524 * @return the CustomerId of the device
525 * @hide
526 */
527 @RequiresPermission(Manifest.permission.BLUETOOTH)
528 public long getHiSyncId(BluetoothDevice device) {
529 if (VDBG) {
530 log("getCustomerId(" + device + ")");
531 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800532 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800533 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800534 if (service == null) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800535 Log.w(TAG, "Proxy not attached to service");
536 return HI_SYNC_ID_INVALID;
537 }
538
539 if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
540
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800541 return service.getHiSyncId(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800542 } catch (RemoteException e) {
543 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
544 return HI_SYNC_ID_INVALID;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800545 }
546 }
547
548 /**
549 * Get the side of the device.
550 *
551 * @param device Bluetooth device.
552 * @return SIDE_LEFT or SIDE_RIGHT
553 * @hide
554 */
555 @RequiresPermission(Manifest.permission.BLUETOOTH)
556 public int getDeviceSide(BluetoothDevice device) {
557 if (VDBG) {
558 log("getDeviceSide(" + device + ")");
559 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800560 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800561 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800562 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800563 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800564 return service.getDeviceSide(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800565 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800566 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800567 return SIDE_LEFT;
568 } catch (RemoteException e) {
569 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
570 return SIDE_LEFT;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800571 }
572 }
573
574 /**
575 * Get the mode of the device.
576 *
577 * @param device Bluetooth device
578 * @return MODE_MONAURAL or MODE_BINAURAL
579 * @hide
580 */
581 @RequiresPermission(Manifest.permission.BLUETOOTH)
582 public int getDeviceMode(BluetoothDevice device) {
583 if (VDBG) {
584 log("getDeviceMode(" + device + ")");
585 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800586 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800587 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800588 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800589 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800590 return service.getDeviceMode(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800591 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800592 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800593 return MODE_MONAURAL;
594 } catch (RemoteException e) {
595 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
596 return MODE_MONAURAL;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800597 }
598 }
599
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800600 private boolean isEnabled() {
601 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
602 return false;
603 }
604
605 private boolean isValidDevice(BluetoothDevice device) {
606 if (device == null) return false;
607
608 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
609 return false;
610 }
611
612 private static void log(String msg) {
613 Log.d(TAG, msg);
614 }
615}