blob: ead8429e4a7509c4adc92db6eada02ac7cb0a287 [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;
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -080025import android.annotation.SystemApi;
Mathew Inwood3a75f262019-06-27 12:47:38 +010026import android.annotation.UnsupportedAppUsage;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080027import android.content.Context;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080028import android.os.Binder;
29import android.os.IBinder;
30import android.os.RemoteException;
31import android.util.Log;
32
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080033import java.util.ArrayList;
34import java.util.List;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080035
36/**
Stanley Tng1f5ea662018-11-15 17:11:36 -080037 * This class provides the public APIs to control the Hearing Aid profile.
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080038 *
39 * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
40 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
41 * the BluetoothHearingAid proxy object.
42 *
Stanley Tng1f5ea662018-11-15 17:11:36 -080043 * <p> Android only supports one set of connected Bluetooth Hearing Aid device at a time. Each
44 * method is protected with its appropriate permission.
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080045 */
46public final class BluetoothHearingAid implements BluetoothProfile {
47 private static final String TAG = "BluetoothHearingAid";
Stanley Tng420a0eb2018-11-15 10:22:07 -080048 private static final boolean DBG = true;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080049 private static final boolean VDBG = false;
50
51 /**
52 * Intent used to broadcast the change in connection state of the Hearing Aid
Stanley Tng1f5ea662018-11-15 17:11:36 -080053 * profile. Please note that in the binaural case, there will be two different LE devices for
54 * the left and right side and each device will have their own connection state changes.S
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080055 *
56 * <p>This intent will have 3 extras:
57 * <ul>
58 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
59 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
60 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
61 * </ul>
62 *
63 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
64 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
65 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
66 *
67 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
68 * receive.
69 */
70 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
71 public static final String ACTION_CONNECTION_STATE_CHANGED =
72 "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
73
74 /**
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080075 * Intent used to broadcast the selection of a connected device as active.
76 *
77 * <p>This intent will have one extra:
78 * <ul>
79 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
80 * be null if no device is active. </li>
81 * </ul>
82 *
83 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
84 * receive.
Stanley Tng1f5ea662018-11-15 17:11:36 -080085 *
86 * @hide
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080087 */
Mathew Inwood3a75f262019-06-27 12:47:38 +010088 @UnsupportedAppUsage
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080089 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
90 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
91 "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
92
93 /**
Stanley Tng1f5ea662018-11-15 17:11:36 -080094 * This device represents Left Hearing Aid.
95 *
96 * @hide
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080097 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080098 public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
99
Stanley Tng1f5ea662018-11-15 17:11:36 -0800100 /**
101 * This device represents Right Hearing Aid.
102 *
103 * @hide
104 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800105 public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
106
Stanley Tng1f5ea662018-11-15 17:11:36 -0800107 /**
108 * This device is Monaural.
109 *
110 * @hide
111 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800112 public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
113
Stanley Tng1f5ea662018-11-15 17:11:36 -0800114 /**
115 * This device is Binaural (should receive only left or right audio).
116 *
117 * @hide
118 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800119 public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
120
Stanley Tng1f5ea662018-11-15 17:11:36 -0800121 /**
122 * Indicates the HiSyncID could not be read and is unavailable.
123 *
124 * @hide
125 */
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800126 public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
127
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800128 private BluetoothAdapter mAdapter;
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800129 private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector =
130 new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID,
131 "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
132 @Override
133 public IBluetoothHearingAid getServiceInterface(IBinder service) {
134 return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800135 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800136 };
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800137
138 /**
139 * Create a BluetoothHearingAid proxy object for interacting with the local
140 * Bluetooth Hearing Aid service.
141 */
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800142 /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800143 mAdapter = BluetoothAdapter.getDefaultAdapter();
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800144 mProfileConnector.connect(context, listener);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800145 }
146
147 /*package*/ void close() {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800148 mProfileConnector.disconnect();
149 }
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800150
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800151 private IBluetoothHearingAid getService() {
152 return mProfileConnector.getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800153 }
154
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800155 /**
156 * Initiate connection to a profile of the remote bluetooth device.
157 *
158 * <p> This API returns false in scenarios like the profile on the
159 * device is already connected or Bluetooth is not turned on.
160 * When this API returns true, it is guaranteed that
161 * connection state intent for the profile will be broadcasted with
162 * the state. Users can get the connection state of the profile
163 * from this intent.
164 *
165 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
166 * permission.
167 *
168 * @param device Remote Bluetooth Device
169 * @return false on immediate error, true otherwise
170 * @hide
171 */
172 public boolean connect(BluetoothDevice device) {
173 if (DBG) log("connect(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800174 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800175 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800176 if (service != null && isEnabled() && isValidDevice(device)) {
177 return service.connect(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800178 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800179 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800180 return false;
181 } catch (RemoteException e) {
182 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
183 return false;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800184 }
185 }
186
187 /**
188 * Initiate disconnection from a profile
189 *
190 * <p> This API will return false in scenarios like the profile on the
191 * Bluetooth device is not in connected state etc. When this API returns,
192 * true, it is guaranteed that the connection state change
193 * intent will be broadcasted with the state. Users can get the
194 * disconnection state of the profile from this intent.
195 *
196 * <p> If the disconnection is initiated by a remote device, the state
197 * will transition from {@link #STATE_CONNECTED} to
198 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
199 * host (local) device the state will transition from
200 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
201 * state {@link #STATE_DISCONNECTED}. The transition to
202 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
203 * two scenarios.
204 *
205 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
206 * permission.
207 *
208 * @param device Remote Bluetooth Device
209 * @return false on immediate error, true otherwise
210 * @hide
211 */
212 public boolean disconnect(BluetoothDevice device) {
213 if (DBG) log("disconnect(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800214 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800215 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800216 if (service != null && isEnabled() && isValidDevice(device)) {
217 return service.disconnect(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800218 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800219 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800220 return false;
221 } catch (RemoteException e) {
222 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
223 return false;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800224 }
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230 @Override
Stanley Tng9d376672019-02-28 12:22:45 -0800231 public @NonNull List<BluetoothDevice> getConnectedDevices() {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800232 if (VDBG) log("getConnectedDevices()");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800233 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800234 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800235 if (service != null && isEnabled()) {
236 return service.getConnectedDevices();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800237 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800238 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800239 return new ArrayList<BluetoothDevice>();
240 } catch (RemoteException e) {
241 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
242 return new ArrayList<BluetoothDevice>();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800243 }
244 }
245
246 /**
247 * {@inheritDoc}
248 */
Stanley Tng7f3b7812019-03-14 16:18:29 -0700249 @Override
250 public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
Stanley Tng9d376672019-02-28 12:22:45 -0800251 @NonNull int[] states) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800252 if (VDBG) log("getDevicesMatchingStates()");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800253 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800254 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800255 if (service != null && isEnabled()) {
256 return service.getDevicesMatchingConnectionStates(states);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800257 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800258 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800259 return new ArrayList<BluetoothDevice>();
260 } catch (RemoteException e) {
261 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
262 return new ArrayList<BluetoothDevice>();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800263 }
264 }
265
266 /**
267 * {@inheritDoc}
268 */
269 @Override
Stanley Tng7f3b7812019-03-14 16:18:29 -0700270 public @BluetoothProfile.BtProfileState int getConnectionState(
271 @NonNull BluetoothDevice device) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800272 if (VDBG) log("getState(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800273 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800274 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800275 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800276 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800277 return service.getConnectionState(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800278 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800279 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800280 return BluetoothProfile.STATE_DISCONNECTED;
281 } catch (RemoteException e) {
282 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
283 return BluetoothProfile.STATE_DISCONNECTED;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800284 }
285 }
286
287 /**
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700288 * Select a connected device as active.
289 *
290 * The active device selection is per profile. An active device's
291 * purpose is profile-specific. For example, Hearing Aid audio
292 * streaming is to the active Hearing Aid device. If a remote device
293 * is not connected, it cannot be selected as active.
294 *
295 * <p> This API returns false in scenarios like the profile on the
296 * device is not connected or Bluetooth is not turned on.
297 * When this API returns true, it is guaranteed that the
298 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
299 * with the active device.
300 *
301 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
302 * permission.
303 *
304 * @param device the remote Bluetooth device. Could be null to clear
305 * the active device and stop streaming audio to a Bluetooth device.
306 * @return false on immediate error, true otherwise
307 * @hide
308 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100309 @UnsupportedAppUsage
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700310 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
311 if (DBG) log("setActiveDevice(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800312 final IBluetoothHearingAid service = getService();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700313 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800314 if (service != null && isEnabled()
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700315 && ((device == null) || isValidDevice(device))) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800316 service.setActiveDevice(device);
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700317 return true;
318 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800319 if (service == null) Log.w(TAG, "Proxy not attached to service");
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700320 return false;
321 } catch (RemoteException e) {
322 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
323 return false;
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700324 }
325 }
326
327 /**
Hansong Zhang8d799f82018-03-28 16:53:10 -0700328 * Get the connected physical Hearing Aid devices that are active
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700329 *
330 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
331 * permission.
332 *
Hansong Zhang8d799f82018-03-28 16:53:10 -0700333 * @return the list of active devices. The first element is the left active
334 * device; the second element is the right active device. If either or both side
335 * is not active, it will be null on that position. Returns empty list on error.
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700336 * @hide
337 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100338 @UnsupportedAppUsage
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700339 @RequiresPermission(Manifest.permission.BLUETOOTH)
Hansong Zhang8d799f82018-03-28 16:53:10 -0700340 public List<BluetoothDevice> getActiveDevices() {
341 if (VDBG) log("getActiveDevices()");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800342 final IBluetoothHearingAid service = getService();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700343 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800344 if (service != null && isEnabled()) {
345 return service.getActiveDevices();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700346 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800347 if (service == null) Log.w(TAG, "Proxy not attached to service");
Hansong Zhang8d799f82018-03-28 16:53:10 -0700348 return new ArrayList<>();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700349 } catch (RemoteException e) {
350 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Hansong Zhang8d799f82018-03-28 16:53:10 -0700351 return new ArrayList<>();
Hansong Zhang7ca303c2018-03-16 09:15:48 -0700352 }
353 }
354
355 /**
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800356 * Set priority of the profile
357 *
358 * <p> The device should already be paired.
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800359 * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800360 *
361 * @param device Paired bluetooth device
362 * @param priority
363 * @return true if priority is set, false on error
364 * @hide
365 */
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800366 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800367 public boolean setPriority(BluetoothDevice device, int priority) {
368 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800369 return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
370 }
371
372 /**
373 * Set connection policy of the profile
374 *
375 * <p> The device should already be paired.
376 * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
377 * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
378 *
379 * @param device Paired bluetooth device
380 * @param connectionPolicy is the connection policy to set to for this profile
381 * @return true if connectionPolicy is set, false on error
382 * @hide
383 */
384 @SystemApi
385 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
386 public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
387 @ConnectionPolicy int connectionPolicy) {
388 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800389 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800390 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800391 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800392 && isValidDevice(device)) {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800393 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
394 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800395 return false;
396 }
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800397 return service.setConnectionPolicy(device, connectionPolicy);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800398 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800399 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800400 return false;
401 } catch (RemoteException e) {
402 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
403 return false;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800404 }
405 }
406
407 /**
408 * Get the priority of the profile.
409 *
410 * <p> The priority can be any of:
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800411 * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800412 *
413 * @param device Bluetooth device
414 * @return priority of the device
415 * @hide
416 */
417 @RequiresPermission(Manifest.permission.BLUETOOTH)
418 public int getPriority(BluetoothDevice device) {
419 if (VDBG) log("getPriority(" + device + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800420 return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
421 }
422
423 /**
424 * Get the connection policy of the profile.
425 *
426 * <p> The connection policy can be any of:
427 * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
428 * {@link #CONNECTION_POLICY_UNKNOWN}
429 *
430 * @param device Bluetooth device
431 * @return connection policy of the device
432 * @hide
433 */
434 @SystemApi
435 @RequiresPermission(Manifest.permission.BLUETOOTH)
436 public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
437 if (VDBG) log("getConnectionPolicy(" + device + ")");
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800438 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800439 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800440 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800441 && isValidDevice(device)) {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800442 return service.getConnectionPolicy(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800443 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800444 if (service == null) Log.w(TAG, "Proxy not attached to service");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800445 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800446 } catch (RemoteException e) {
447 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800448 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800449 }
450 }
451
452 /**
453 * Helper for converting a state to a string.
454 *
455 * For debug use only - strings are not internationalized.
456 *
457 * @hide
458 */
459 public static String stateToString(int state) {
460 switch (state) {
461 case STATE_DISCONNECTED:
462 return "disconnected";
463 case STATE_CONNECTING:
464 return "connecting";
465 case STATE_CONNECTED:
466 return "connected";
467 case STATE_DISCONNECTING:
468 return "disconnecting";
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800469 default:
470 return "<unknown state " + state + ">";
471 }
472 }
473
474 /**
475 * Get the volume of the device.
476 *
477 * <p> The volume is between -128 dB (mute) to 0 dB.
478 *
479 * @return volume of the hearing aid device.
480 * @hide
481 */
482 @RequiresPermission(Manifest.permission.BLUETOOTH)
483 public int getVolume() {
484 if (VDBG) {
485 log("getVolume()");
486 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800487 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800488 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800489 if (service != null && isEnabled()) {
490 return service.getVolume();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800491 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800492 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800493 return 0;
494 } catch (RemoteException e) {
495 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
496 return 0;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800497 }
498 }
499
500 /**
501 * Tells remote device to adjust volume. Uses the following values:
502 * <ul>
503 * <li>{@link AudioManager#ADJUST_LOWER}</li>
504 * <li>{@link AudioManager#ADJUST_RAISE}</li>
505 * <li>{@link AudioManager#ADJUST_MUTE}</li>
506 * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
507 * </ul>
508 *
509 * @param direction One of the supported adjust values.
510 * @hide
511 */
512 @RequiresPermission(Manifest.permission.BLUETOOTH)
513 public void adjustVolume(int direction) {
514 if (DBG) log("adjustVolume(" + direction + ")");
515
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800516 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800517 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800518 if (service == null) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800519 Log.w(TAG, "Proxy not attached to service");
520 return;
521 }
522
523 if (!isEnabled()) return;
524
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800525 service.adjustVolume(direction);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800526 } catch (RemoteException e) {
527 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800528 }
529 }
530
531 /**
532 * Tells remote device to set an absolute volume.
533 *
534 * @param volume Absolute volume to be set on remote
535 * @hide
536 */
537 public void setVolume(int volume) {
538 if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
539
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800540 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800541 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800542 if (service == null) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800543 Log.w(TAG, "Proxy not attached to service");
544 return;
545 }
546
547 if (!isEnabled()) return;
548
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800549 service.setVolume(volume);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800550 } catch (RemoteException e) {
551 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800552 }
553 }
554
555 /**
556 * Get the CustomerId of the device.
557 *
558 * @param device Bluetooth device
559 * @return the CustomerId of the device
560 * @hide
561 */
562 @RequiresPermission(Manifest.permission.BLUETOOTH)
563 public long getHiSyncId(BluetoothDevice device) {
564 if (VDBG) {
565 log("getCustomerId(" + device + ")");
566 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800567 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800568 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800569 if (service == null) {
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800570 Log.w(TAG, "Proxy not attached to service");
571 return HI_SYNC_ID_INVALID;
572 }
573
574 if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
575
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800576 return service.getHiSyncId(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800577 } catch (RemoteException e) {
578 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
579 return HI_SYNC_ID_INVALID;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800580 }
581 }
582
583 /**
584 * Get the side of the device.
585 *
586 * @param device Bluetooth device.
587 * @return SIDE_LEFT or SIDE_RIGHT
588 * @hide
589 */
590 @RequiresPermission(Manifest.permission.BLUETOOTH)
591 public int getDeviceSide(BluetoothDevice device) {
592 if (VDBG) {
593 log("getDeviceSide(" + device + ")");
594 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800595 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800596 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800597 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800598 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800599 return service.getDeviceSide(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800600 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800601 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800602 return SIDE_LEFT;
603 } catch (RemoteException e) {
604 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
605 return SIDE_LEFT;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800606 }
607 }
608
609 /**
610 * Get the mode of the device.
611 *
612 * @param device Bluetooth device
613 * @return MODE_MONAURAL or MODE_BINAURAL
614 * @hide
615 */
616 @RequiresPermission(Manifest.permission.BLUETOOTH)
617 public int getDeviceMode(BluetoothDevice device) {
618 if (VDBG) {
619 log("getDeviceMode(" + device + ")");
620 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800621 final IBluetoothHearingAid service = getService();
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800622 try {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800623 if (service != null && isEnabled()
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800624 && isValidDevice(device)) {
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800625 return service.getDeviceMode(device);
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800626 }
Ugo Yu6d9cfce2019-03-26 21:38:08 +0800627 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800628 return MODE_MONAURAL;
629 } catch (RemoteException e) {
630 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
631 return MODE_MONAURAL;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800632 }
633 }
634
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800635 private boolean isEnabled() {
636 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
637 return false;
638 }
639
640 private boolean isValidDevice(BluetoothDevice device) {
641 if (device == null) return false;
642
643 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
644 return false;
645 }
646
647 private static void log(String msg) {
648 Log.d(TAG, msg);
649 }
650}