blob: fdfc6beca3fdd811c0a585e50101630b28f124c2 [file] [log] [blame]
The Android Open Source Project33897762009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Jack Hec46a01e2018-05-02 19:10:56 -070019import android.Manifest;
Rahul Sabnise8bac9b2019-11-27 18:09:33 -080020import android.annotation.NonNull;
Jack He889d2342018-01-03 12:13:26 -080021import android.annotation.Nullable;
Selim Guruna117cb372017-10-17 17:01:38 -070022import android.annotation.RequiresPermission;
Nick Pellydac4c0d2009-09-10 10:21:56 -070023import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
Selim Guruna117cb372017-10-17 17:01:38 -070025import android.annotation.SystemApi;
Artur Satayev3625be42019-12-10 17:47:52 +000026import android.compat.annotation.UnsupportedAppUsage;
The Android Open Source Project33897762009-03-03 19:31:44 -080027import android.content.ComponentName;
28import android.content.Context;
Jeff Sharkey73458a82016-11-04 11:23:46 -060029import android.os.Binder;
Mathew Inwood049f0f52020-11-04 09:29:36 +000030import android.os.Build;
Benjamin Franz3362fef2014-11-12 15:57:54 +000031import android.os.Handler;
The Android Open Source Project33897762009-03-03 19:31:44 -080032import android.os.IBinder;
Benjamin Franz3362fef2014-11-12 15:57:54 +000033import android.os.Looper;
34import android.os.Message;
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -070035import android.os.RemoteException;
The Android Open Source Project33897762009-03-03 19:31:44 -080036import android.util.Log;
37
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -070038import java.util.ArrayList;
39import java.util.List;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070040
The Android Open Source Project33897762009-03-03 19:31:44 -080041/**
The Android Open Source Project33897762009-03-03 19:31:44 -080042 * Public API for controlling the Bluetooth Headset Service. This includes both
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070043 * Bluetooth Headset and Handsfree (v1.5) profiles.
The Android Open Source Project33897762009-03-03 19:31:44 -080044 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070045 * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
The Android Open Source Project33897762009-03-03 19:31:44 -080046 * Service via IPC.
47 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070048 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
49 * the BluetoothHeadset proxy object. Use
50 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
The Android Open Source Project33897762009-03-03 19:31:44 -080051 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070052 * <p> Android only supports one connected Bluetooth Headset at a time.
53 * Each method is protected with its appropriate permission.
The Android Open Source Project33897762009-03-03 19:31:44 -080054 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070055public final class BluetoothHeadset implements BluetoothProfile {
The Android Open Source Project33897762009-03-03 19:31:44 -080056 private static final String TAG = "BluetoothHeadset";
fredc3c719642012-04-12 00:02:00 -070057 private static final boolean DBG = true;
Matthew Xief8035a72012-10-09 22:10:37 -070058 private static final boolean VDBG = false;
The Android Open Source Project33897762009-03-03 19:31:44 -080059
Nick Pellydac4c0d2009-09-10 10:21:56 -070060 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070061 * Intent used to broadcast the change in connection state of the Headset
62 * profile.
63 *
64 * <p>This intent will have 3 extras:
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080065 * <ul>
Jack He910201b2017-08-22 16:06:54 -070066 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
67 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
68 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080069 * </ul>
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -080070 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070071 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
72 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
73 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080074 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
75 * receive.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070076 */
77 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
78 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack He910201b2017-08-22 16:06:54 -070079 "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070080
81 /**
82 * Intent used to broadcast the change in the Audio Connection state of the
83 * A2DP profile.
84 *
85 * <p>This intent will have 3 extras:
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080086 * <ul>
Jack He910201b2017-08-22 16:06:54 -070087 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
88 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
89 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080090 * </ul>
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -080091 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070092 * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
93 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080094 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
95 * to receive.
Nick Pellydac4c0d2009-09-10 10:21:56 -070096 */
97 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
98 public static final String ACTION_AUDIO_STATE_CHANGED =
Jack He910201b2017-08-22 16:06:54 -070099 "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
Nick Pellydac4c0d2009-09-10 10:21:56 -0700100
Jack He889d2342018-01-03 12:13:26 -0800101 /**
102 * Intent used to broadcast the selection of a connected device as active.
103 *
104 * <p>This intent will have one extra:
105 * <ul>
106 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
107 * be null if no device is active. </li>
108 * </ul>
109 *
110 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
111 * receive.
112 *
113 * @hide
114 */
115 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwoodb426f902021-01-06 12:05:47 +0000116 @UnsupportedAppUsage(trackingBug = 171933273)
Jack He889d2342018-01-03 12:13:26 -0800117 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
118 "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
Jaikumar Ganeshf48cda52010-04-02 14:44:43 -0700119
Nick Pellydac4c0d2009-09-10 10:21:56 -0700120 /**
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700121 * Intent used to broadcast that the headset has posted a
122 * vendor-specific event.
123 *
124 * <p>This intent will have 4 extras and 1 category.
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800125 * <ul>
Jack He910201b2017-08-22 16:06:54 -0700126 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
127 * </li>
128 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
129 * specific command </li>
130 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
131 * command type which can be one of {@link #AT_CMD_TYPE_READ},
132 * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
133 * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
134 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
135 * arguments. </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800136 * </ul>
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700137 *
Jack He910201b2017-08-22 16:06:54 -0700138 * <p> The category is the Company ID of the vendor defining the
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700139 * vendor-specific command. {@link BluetoothAssignedNumbers}
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700140 *
141 * For example, for Plantronics specific events
142 * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
143 *
144 * <p> For example, an AT+XEVENT=foo,3 will get translated into
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800145 * <ul>
Jack He910201b2017-08-22 16:06:54 -0700146 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
147 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
148 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800149 * </ul>
150 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
151 * to receive.
Herb Jellinekaad41c52010-08-10 13:17:43 -0700152 */
153 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
154 public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
155 "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
156
157 /**
158 * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
159 * intents that contains the name of the vendor-specific command.
160 */
161 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
162 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
163
164 /**
165 * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700166 * intents that contains the AT command type of the vendor-specific command.
Herb Jellinekaad41c52010-08-10 13:17:43 -0700167 */
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700168 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
169 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
170
171 /**
172 * AT command type READ used with
173 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
174 * For example, AT+VGM?. There are no arguments for this command type.
175 */
176 public static final int AT_CMD_TYPE_READ = 0;
177
178 /**
179 * AT command type TEST used with
180 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
181 * For example, AT+VGM=?. There are no arguments for this command type.
182 */
183 public static final int AT_CMD_TYPE_TEST = 1;
184
185 /**
186 * AT command type SET used with
187 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
188 * For example, AT+VGM=<args>.
189 */
190 public static final int AT_CMD_TYPE_SET = 2;
191
192 /**
193 * AT command type BASIC used with
194 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
195 * For example, ATD. Single character commands and everything following the
196 * character are arguments.
197 */
198 public static final int AT_CMD_TYPE_BASIC = 3;
199
200 /**
201 * AT command type ACTION used with
202 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
203 * For example, AT+CHUP. There are no arguments for action commands.
204 */
205 public static final int AT_CMD_TYPE_ACTION = 4;
Herb Jellinekaad41c52010-08-10 13:17:43 -0700206
207 /**
208 * A Parcelable String array extra field in
209 * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
210 * the arguments to the vendor-specific command.
211 */
212 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
213 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
214
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700215 /**
216 * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
217 * for the companyId
218 */
Jack He910201b2017-08-22 16:06:54 -0700219 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700220 "android.bluetooth.headset.intent.category.companyid";
221
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700222 /**
Edward Jee7c81f1f2013-08-16 04:07:49 -0700223 * A vendor-specific command for unsolicited result code.
224 */
225 public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
226
227 /**
Jack He7f9c70b2017-06-20 17:07:40 -0700228 * A vendor-specific AT command
Jack He910201b2017-08-22 16:06:54 -0700229 *
Jack He7f9c70b2017-06-20 17:07:40 -0700230 * @hide
231 */
Jack He0dfd69b2017-06-20 17:09:47 -0700232 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
233
234 /**
235 * A vendor-specific AT command
Jack He910201b2017-08-22 16:06:54 -0700236 *
Jack He0dfd69b2017-06-20 17:09:47 -0700237 * @hide
238 */
239 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
240
241 /**
242 * Battery level indicator associated with
243 * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
Jack He910201b2017-08-22 16:06:54 -0700244 *
Jack He0dfd69b2017-06-20 17:09:47 -0700245 * @hide
246 */
247 public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
248
249 /**
250 * A vendor-specific AT command
Jack He910201b2017-08-22 16:06:54 -0700251 *
Jack He0dfd69b2017-06-20 17:09:47 -0700252 * @hide
253 */
Jack He7f9c70b2017-06-20 17:07:40 -0700254 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
255
256 /**
257 * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
Jack He910201b2017-08-22 16:06:54 -0700258 *
Jack He7f9c70b2017-06-20 17:07:40 -0700259 * @hide
260 */
261 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
262
263 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800264 * Headset state when SCO audio is not connected.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700265 * This state can be one of
266 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
267 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
268 */
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800269 public static final int STATE_AUDIO_DISCONNECTED = 10;
Herb Jellinekaad41c52010-08-10 13:17:43 -0700270
271 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800272 * Headset state when SCO audio is connecting.
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700273 * This state can be one of
274 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
275 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700276 */
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800277 public static final int STATE_AUDIO_CONNECTING = 11;
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700278
279 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800280 * Headset state when SCO audio is connected.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700281 * This state can be one of
282 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
283 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Nick Pellydac4c0d2009-09-10 10:21:56 -0700284 */
Chienyuan0d3bada2019-05-29 10:29:30 +0800285 public static final int STATE_AUDIO_CONNECTED = 12;
Mudumba Ananth3246de62016-02-29 02:14:36 -0800286
287 /**
288 * Intent used to broadcast the headset's indicator status
289 *
290 * <p>This intent will have 3 extras:
291 * <ul>
Jack He910201b2017-08-22 16:06:54 -0700292 * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
293 * is supported by the headset ( as indicated by AT+BIND command in the SLC
294 * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
295 * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
296 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
Mudumba Ananth3246de62016-02-29 02:14:36 -0800297 * </ul>
Jack He48f6a8a2017-06-22 12:56:54 -0700298 * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
Jack He910201b2017-08-22 16:06:54 -0700299 * are given an assigned number. Below shows the assigned number of Indicator added so far
Jack He48f6a8a2017-06-22 12:56:54 -0700300 * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
301 * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
302 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
Jack He910201b2017-08-22 16:06:54 -0700303 *
Mudumba Ananth3246de62016-02-29 02:14:36 -0800304 * @hide
305 */
306 public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
307 "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
308
309 /**
Jack He48f6a8a2017-06-22 12:56:54 -0700310 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
311 * intents that contains the assigned number of the headset indicator as defined by
312 * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
Jack He910201b2017-08-22 16:06:54 -0700313 *
Mudumba Ananth3246de62016-02-29 02:14:36 -0800314 * @hide
315 */
316 public static final String EXTRA_HF_INDICATORS_IND_ID =
317 "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
318
319 /**
Jack He48f6a8a2017-06-22 12:56:54 -0700320 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
Mudumba Ananth3246de62016-02-29 02:14:36 -0800321 * intents that contains the value of the Headset indicator that is being sent.
Jack He910201b2017-08-22 16:06:54 -0700322 *
Mudumba Ananth3246de62016-02-29 02:14:36 -0800323 * @hide
324 */
325 public static final String EXTRA_HF_INDICATORS_IND_VALUE =
326 "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
327
Benjamin Franz3362fef2014-11-12 15:57:54 +0000328 private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
329 private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700330
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700331 private Context mContext;
332 private ServiceListener mServiceListener;
Jack He1f686f62017-08-17 12:11:18 -0700333 private volatile IBluetoothHeadset mService;
Matthew Xie5e9fb032012-03-21 23:15:06 -0700334 private BluetoothAdapter mAdapter;
The Android Open Source Project33897762009-03-03 19:31:44 -0800335
Jack He9e045d22017-08-22 21:21:23 -0700336 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc3c719642012-04-12 00:02:00 -0700337 new IBluetoothStateChangeCallback.Stub() {
338 public void onBluetoothStateChange(boolean up) {
339 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
340 if (!up) {
Benjamin Franz3362fef2014-11-12 15:57:54 +0000341 doUnbind();
fredc3c719642012-04-12 00:02:00 -0700342 } else {
Ugo Yu1652b942019-03-26 21:38:08 +0800343 doBind();
fredc3c719642012-04-12 00:02:00 -0700344 }
345 }
Jack He910201b2017-08-22 16:06:54 -0700346 };
fredc3c719642012-04-12 00:02:00 -0700347
The Android Open Source Project33897762009-03-03 19:31:44 -0800348 /**
349 * Create a BluetoothHeadset proxy object.
350 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700351 /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800352 mContext = context;
353 mServiceListener = l;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700354 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc3c719642012-04-12 00:02:00 -0700355
356 IBluetoothManager mgr = mAdapter.getBluetoothManager();
357 if (mgr != null) {
358 try {
359 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
360 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700361 Log.e(TAG, "", e);
fredc3c719642012-04-12 00:02:00 -0700362 }
363 }
364
Dianne Hackborn3875ec62013-08-04 16:50:16 -0700365 doBind();
366 }
367
Ugo Yu1652b942019-03-26 21:38:08 +0800368 private boolean doBind() {
369 synchronized (mConnection) {
370 if (mService == null) {
371 if (VDBG) Log.d(TAG, "Binding service...");
372 try {
373 return mAdapter.getBluetoothManager().bindBluetoothProfileService(
374 BluetoothProfile.HEADSET, mConnection);
375 } catch (RemoteException e) {
376 Log.e(TAG, "Unable to bind HeadsetService", e);
377 }
378 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800379 }
Benjamin Franz3362fef2014-11-12 15:57:54 +0000380 return false;
381 }
382
Ugo Yu1652b942019-03-26 21:38:08 +0800383 private void doUnbind() {
Benjamin Franz3362fef2014-11-12 15:57:54 +0000384 synchronized (mConnection) {
385 if (mService != null) {
Ugo Yu1652b942019-03-26 21:38:08 +0800386 if (VDBG) Log.d(TAG, "Unbinding service...");
Benjamin Franz3362fef2014-11-12 15:57:54 +0000387 try {
388 mAdapter.getBluetoothManager().unbindBluetoothProfileService(
389 BluetoothProfile.HEADSET, mConnection);
390 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700391 Log.e(TAG, "Unable to unbind HeadsetService", e);
Ugo Yu1652b942019-03-26 21:38:08 +0800392 } finally {
393 mService = null;
Benjamin Franz3362fef2014-11-12 15:57:54 +0000394 }
395 }
396 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800397 }
398
The Android Open Source Project33897762009-03-03 19:31:44 -0800399 /**
400 * Close the connection to the backing service.
401 * Other public functions of BluetoothHeadset will return default error
402 * results once close() has been called. Multiple invocations of close()
403 * are ok.
404 */
Mathew Inwood7d543892018-08-01 15:07:20 +0100405 @UnsupportedAppUsage
Matthew Xie78912492012-03-22 17:18:37 -0700406 /*package*/ void close() {
Matthew Xief8035a72012-10-09 22:10:37 -0700407 if (VDBG) log("close()");
fredc3c719642012-04-12 00:02:00 -0700408
409 IBluetoothManager mgr = mAdapter.getBluetoothManager();
410 if (mgr != null) {
411 try {
412 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
Ugo Yu1652b942019-03-26 21:38:08 +0800413 } catch (RemoteException re) {
414 Log.e(TAG, "", re);
fredc3c719642012-04-12 00:02:00 -0700415 }
416 }
Benjamin Franz733656c2014-12-16 15:33:03 +0000417 mServiceListener = null;
Benjamin Franz3362fef2014-11-12 15:57:54 +0000418 doUnbind();
The Android Open Source Project33897762009-03-03 19:31:44 -0800419 }
420
421 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700422 * Initiate connection to a profile of the remote bluetooth device.
423 *
424 * <p> Currently, the system supports only 1 connection to the
425 * headset/handsfree profile. The API will automatically disconnect connected
426 * devices before connecting.
427 *
428 * <p> This API returns false in scenarios like the profile on the
429 * device is already connected or Bluetooth is not turned on.
430 * When this API returns true, it is guaranteed that
431 * connection state intent for the profile will be broadcasted with
432 * the state. Users can get the connection state of the profile
433 * from this intent.
434 *
435 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
436 * permission.
437 *
438 * @param device Remote Bluetooth Device
Jack He910201b2017-08-22 16:06:54 -0700439 * @return false on immediate error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700440 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800441 */
Selim Guruna117cb372017-10-17 17:01:38 -0700442 @SystemApi
443 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700444 public boolean connect(BluetoothDevice device) {
445 if (DBG) log("connect(" + device + ")");
Jack He1f686f62017-08-17 12:11:18 -0700446 final IBluetoothHeadset service = mService;
447 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800448 try {
Jack He1f686f62017-08-17 12:11:18 -0700449 return service.connect(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700450 } catch (RemoteException e) {
451 Log.e(TAG, Log.getStackTraceString(new Throwable()));
452 return false;
453 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800454 }
Jack He1f686f62017-08-17 12:11:18 -0700455 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800456 return false;
457 }
458
459 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700460 * Initiate disconnection from a profile
461 *
462 * <p> This API will return false in scenarios like the profile on the
463 * Bluetooth device is not in connected state etc. When this API returns,
464 * true, it is guaranteed that the connection state change
465 * intent will be broadcasted with the state. Users can get the
466 * disconnection state of the profile from this intent.
467 *
468 * <p> If the disconnection is initiated by a remote device, the state
469 * will transition from {@link #STATE_CONNECTED} to
470 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
471 * host (local) device the state will transition from
472 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
473 * state {@link #STATE_DISCONNECTED}. The transition to
474 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
475 * two scenarios.
476 *
477 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
478 * permission.
479 *
480 * @param device Remote Bluetooth Device
Jack He910201b2017-08-22 16:06:54 -0700481 * @return false on immediate error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700482 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800483 */
Selim Guruna117cb372017-10-17 17:01:38 -0700484 @SystemApi
485 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700486 public boolean disconnect(BluetoothDevice device) {
487 if (DBG) log("disconnect(" + device + ")");
Jack He1f686f62017-08-17 12:11:18 -0700488 final IBluetoothHeadset service = mService;
489 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800490 try {
Jack He1f686f62017-08-17 12:11:18 -0700491 return service.disconnect(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700492 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700493 Log.e(TAG, Log.getStackTraceString(new Throwable()));
494 return false;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700495 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800496 }
Jack He1f686f62017-08-17 12:11:18 -0700497 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800498 return false;
499 }
500
501 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700502 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800503 */
Jack He9e045d22017-08-22 21:21:23 -0700504 @Override
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700505 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xief8035a72012-10-09 22:10:37 -0700506 if (VDBG) log("getConnectedDevices()");
Jack He1f686f62017-08-17 12:11:18 -0700507 final IBluetoothHeadset service = mService;
508 if (service != null && isEnabled()) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800509 try {
Jack He1f686f62017-08-17 12:11:18 -0700510 return service.getConnectedDevices();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700511 } catch (RemoteException e) {
512 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700513 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700514 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800515 }
Jack He1f686f62017-08-17 12:11:18 -0700516 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700517 return new ArrayList<BluetoothDevice>();
The Android Open Source Project33897762009-03-03 19:31:44 -0800518 }
519
520 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700521 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800522 */
Jack He9e045d22017-08-22 21:21:23 -0700523 @Override
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700524 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xief8035a72012-10-09 22:10:37 -0700525 if (VDBG) log("getDevicesMatchingStates()");
Jack He1f686f62017-08-17 12:11:18 -0700526 final IBluetoothHeadset service = mService;
527 if (service != null && isEnabled()) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800528 try {
Jack He1f686f62017-08-17 12:11:18 -0700529 return service.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700530 } catch (RemoteException e) {
531 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700532 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700533 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800534 }
Jack He1f686f62017-08-17 12:11:18 -0700535 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700536 return new ArrayList<BluetoothDevice>();
The Android Open Source Project33897762009-03-03 19:31:44 -0800537 }
538
539 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700540 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800541 */
Jack He9e045d22017-08-22 21:21:23 -0700542 @Override
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700543 public int getConnectionState(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700544 if (VDBG) log("getConnectionState(" + device + ")");
Jack He1f686f62017-08-17 12:11:18 -0700545 final IBluetoothHeadset service = mService;
546 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800547 try {
Jack He1f686f62017-08-17 12:11:18 -0700548 return service.getConnectionState(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700549 } catch (RemoteException e) {
550 Log.e(TAG, Log.getStackTraceString(new Throwable()));
551 return BluetoothProfile.STATE_DISCONNECTED;
552 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800553 }
Jack He1f686f62017-08-17 12:11:18 -0700554 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700555 return BluetoothProfile.STATE_DISCONNECTED;
The Android Open Source Project33897762009-03-03 19:31:44 -0800556 }
557
558 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700559 * Set priority of the profile
560 *
561 * <p> The device should already be paired.
Selim Gurun9e6b35b2018-01-09 14:35:19 -0800562 * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
Rahul Sabnisd9798612019-12-04 14:21:10 -0800563 * {@link BluetoothProfile#PRIORITY_OFF}
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700564 *
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700565 * @param device Paired bluetooth device
566 * @param priority
567 * @return true if priority is set, false on error
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700568 * @hide
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800569 * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
The Android Open Source Project33897762009-03-03 19:31:44 -0800570 */
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800571 @Deprecated
Selim Guruna117cb372017-10-17 17:01:38 -0700572 @SystemApi
573 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Nick Pelly2d664882009-08-14 18:33:38 -0700574 public boolean setPriority(BluetoothDevice device, int priority) {
575 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Rahul Sabnisc228ce22020-03-18 17:46:33 -0700576 final IBluetoothHeadset service = mService;
577 if (service != null && isEnabled() && isValidDevice(device)) {
578 if (priority != BluetoothProfile.PRIORITY_OFF
579 && priority != BluetoothProfile.PRIORITY_ON) {
580 return false;
581 }
582 try {
583 return service.setPriority(
584 device, BluetoothAdapter.priorityToConnectionPolicy(priority));
585 } catch (RemoteException e) {
586 Log.e(TAG, Log.getStackTraceString(new Throwable()));
587 return false;
588 }
589 }
590 if (service == null) Log.w(TAG, "Proxy not attached to service");
591 return false;
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800592 }
593
594 /**
595 * Set connection policy of the profile
596 *
597 * <p> The device should already be paired.
598 * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
599 * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
600 *
601 * @param device Paired bluetooth device
602 * @param connectionPolicy is the connection policy to set to for this profile
603 * @return true if connectionPolicy is set, false on error
604 * @hide
605 */
606 @SystemApi
Rahul Sabnisc228ce22020-03-18 17:46:33 -0700607 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800608 public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
609 @ConnectionPolicy int connectionPolicy) {
610 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
Jack He1f686f62017-08-17 12:11:18 -0700611 final IBluetoothHeadset service = mService;
612 if (service != null && isEnabled() && isValidDevice(device)) {
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800613 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
614 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
Jack He910201b2017-08-22 16:06:54 -0700615 return false;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700616 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800617 try {
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800618 return service.setConnectionPolicy(device, connectionPolicy);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700619 } catch (RemoteException e) {
620 Log.e(TAG, Log.getStackTraceString(new Throwable()));
621 return false;
622 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800623 }
Jack He1f686f62017-08-17 12:11:18 -0700624 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800625 return false;
626 }
627
628 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700629 * Get the priority of the profile.
630 *
631 * <p> The priority can be any of:
632 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
633 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
634 *
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700635 * @param device Bluetooth device
636 * @return priority of the device
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700637 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800638 */
Mathew Inwood049f0f52020-11-04 09:29:36 +0000639 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800640 @RequiresPermission(Manifest.permission.BLUETOOTH)
Nick Pelly2d664882009-08-14 18:33:38 -0700641 public int getPriority(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700642 if (VDBG) log("getPriority(" + device + ")");
Rahul Sabnisc228ce22020-03-18 17:46:33 -0700643 final IBluetoothHeadset service = mService;
644 if (service != null && isEnabled() && isValidDevice(device)) {
645 try {
646 return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device));
647 } catch (RemoteException e) {
648 Log.e(TAG, Log.getStackTraceString(new Throwable()));
649 return BluetoothProfile.PRIORITY_OFF;
650 }
651 }
652 if (service == null) Log.w(TAG, "Proxy not attached to service");
653 return BluetoothProfile.PRIORITY_OFF;
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800654 }
655
656 /**
657 * Get the connection policy of the profile.
658 *
659 * <p> The connection policy can be any of:
660 * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
661 * {@link #CONNECTION_POLICY_UNKNOWN}
662 *
663 * @param device Bluetooth device
664 * @return connection policy of the device
665 * @hide
666 */
667 @SystemApi
Rahul Sabnisc228ce22020-03-18 17:46:33 -0700668 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800669 public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
670 if (VDBG) log("getConnectionPolicy(" + device + ")");
Jack He1f686f62017-08-17 12:11:18 -0700671 final IBluetoothHeadset service = mService;
672 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800673 try {
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800674 return service.getConnectionPolicy(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700675 } catch (RemoteException e) {
676 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800677 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700678 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800679 }
Jack He1f686f62017-08-17 12:11:18 -0700680 if (service == null) Log.w(TAG, "Proxy not attached to service");
Rahul Sabnise8bac9b2019-11-27 18:09:33 -0800681 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700682 }
683
684 /**
685 * Start Bluetooth voice recognition. This methods sends the voice
686 * recognition AT command to the headset and establishes the
687 * audio connection.
688 *
689 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800690 * If this function returns true, this intent will be broadcasted with
691 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700692 *
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800693 * <p> {@link #EXTRA_STATE} will transition from
694 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
695 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
696 * in case of failure to establish the audio connection.
697 *
698 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700699 *
700 * @param device Bluetooth headset
Jack Hec46a01e2018-05-02 19:10:56 -0700701 * @return false if there is no headset connected, or the connected headset doesn't support
702 * voice recognition, or voice recognition is already started, or audio channel is occupied,
703 * or on error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700704 */
705 public boolean startVoiceRecognition(BluetoothDevice device) {
706 if (DBG) log("startVoiceRecognition()");
Jack He1f686f62017-08-17 12:11:18 -0700707 final IBluetoothHeadset service = mService;
708 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700709 try {
Jack He1f686f62017-08-17 12:11:18 -0700710 return service.startVoiceRecognition(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700711 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700712 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700713 }
714 }
Jack He1f686f62017-08-17 12:11:18 -0700715 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700716 return false;
717 }
718
719 /**
720 * Stop Bluetooth Voice Recognition mode, and shut down the
721 * Bluetooth audio path.
722 *
Jack Hec46a01e2018-05-02 19:10:56 -0700723 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
724 * If this function returns true, this intent will be broadcasted with
725 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
726 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800727 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700728 *
729 * @param device Bluetooth headset
Jack Hec46a01e2018-05-02 19:10:56 -0700730 * @return false if there is no headset connected, or voice recognition has not started,
731 * or voice recognition has ended on this headset, or on error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700732 */
733 public boolean stopVoiceRecognition(BluetoothDevice device) {
734 if (DBG) log("stopVoiceRecognition()");
Jack He1f686f62017-08-17 12:11:18 -0700735 final IBluetoothHeadset service = mService;
736 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700737 try {
Jack He1f686f62017-08-17 12:11:18 -0700738 return service.stopVoiceRecognition(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700739 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700740 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700741 }
742 }
Jack He1f686f62017-08-17 12:11:18 -0700743 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700744 return false;
745 }
746
747 /**
748 * Check if Bluetooth SCO audio is connected.
749 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800750 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700751 *
752 * @param device Bluetooth headset
Jack He910201b2017-08-22 16:06:54 -0700753 * @return true if SCO is connected, false otherwise or on error
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700754 */
755 public boolean isAudioConnected(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700756 if (VDBG) log("isAudioConnected()");
Jack He1f686f62017-08-17 12:11:18 -0700757 final IBluetoothHeadset service = mService;
758 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700759 try {
Jack He1f686f62017-08-17 12:11:18 -0700760 return service.isAudioConnected(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700761 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700762 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700763 }
764 }
Jack He1f686f62017-08-17 12:11:18 -0700765 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700766 return false;
The Android Open Source Project33897762009-03-03 19:31:44 -0800767 }
768
769 /**
Eric Laurent2e66fa22010-03-17 14:59:27 -0700770 * Indicates if current platform supports voice dialing over bluetooth SCO.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700771 *
Eric Laurent2e66fa22010-03-17 14:59:27 -0700772 * @return true if voice dialing over bluetooth is supported, false otherwise.
773 * @hide
774 */
775 public static boolean isBluetoothVoiceDialingEnabled(Context context) {
776 return context.getResources().getBoolean(
777 com.android.internal.R.bool.config_bluetooth_sco_off_call);
778 }
779
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700780 /**
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700781 * Get the current audio state of the Headset.
782 * Note: This is an internal function and shouldn't be exposed
783 *
784 * @hide
785 */
Mathew Inwood049f0f52020-11-04 09:29:36 +0000786 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700787 public int getAudioState(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700788 if (VDBG) log("getAudioState");
Jack He1f686f62017-08-17 12:11:18 -0700789 final IBluetoothHeadset service = mService;
790 if (service != null && !isDisabled()) {
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700791 try {
Jack He1f686f62017-08-17 12:11:18 -0700792 return service.getAudioState(device);
Jack He910201b2017-08-22 16:06:54 -0700793 } catch (RemoteException e) {
794 Log.e(TAG, e.toString());
795 }
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700796 } else {
797 Log.w(TAG, "Proxy not attached to service");
798 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
799 }
800 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
801 }
802
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700803 /**
Bryce Lee0e154a32015-11-16 08:55:52 -0800804 * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
805 * audio to the HF unless explicitly told to.
806 * This method should be used in cases where the SCO channel is shared between multiple profiles
807 * and must be delegated by a source knowledgeable
808 * Note: This is an internal function and shouldn't be exposed
809 *
810 * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
Bryce Lee0e154a32015-11-16 08:55:52 -0800811 * @hide
812 */
813 public void setAudioRouteAllowed(boolean allowed) {
814 if (VDBG) log("setAudioRouteAllowed");
Jack He1f686f62017-08-17 12:11:18 -0700815 final IBluetoothHeadset service = mService;
816 if (service != null && isEnabled()) {
Bryce Lee0e154a32015-11-16 08:55:52 -0800817 try {
Jack He1f686f62017-08-17 12:11:18 -0700818 service.setAudioRouteAllowed(allowed);
Jack He910201b2017-08-22 16:06:54 -0700819 } catch (RemoteException e) {
820 Log.e(TAG, e.toString());
821 }
Bryce Lee0e154a32015-11-16 08:55:52 -0800822 } else {
823 Log.w(TAG, "Proxy not attached to service");
824 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
825 }
826 }
827
828 /**
829 * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
830 * Note: This is an internal function and shouldn't be exposed
831 *
832 * @hide
833 */
834 public boolean getAudioRouteAllowed() {
835 if (VDBG) log("getAudioRouteAllowed");
Jack He1f686f62017-08-17 12:11:18 -0700836 final IBluetoothHeadset service = mService;
837 if (service != null && isEnabled()) {
Bryce Lee0e154a32015-11-16 08:55:52 -0800838 try {
Jack He1f686f62017-08-17 12:11:18 -0700839 return service.getAudioRouteAllowed();
Jack He910201b2017-08-22 16:06:54 -0700840 } catch (RemoteException e) {
841 Log.e(TAG, e.toString());
842 }
Bryce Lee0e154a32015-11-16 08:55:52 -0800843 } else {
844 Log.w(TAG, "Proxy not attached to service");
845 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
846 }
847 return false;
848 }
849
850 /**
Jack He798d7282017-05-09 17:16:01 -0700851 * Force SCO audio to be opened regardless any other restrictions
852 *
Jack He910201b2017-08-22 16:06:54 -0700853 * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio
854 * False to use SCO audio in normal manner
Jack He798d7282017-05-09 17:16:01 -0700855 * @hide
856 */
857 public void setForceScoAudio(boolean forced) {
858 if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
Jack He1f686f62017-08-17 12:11:18 -0700859 final IBluetoothHeadset service = mService;
860 if (service != null && isEnabled()) {
Jack He798d7282017-05-09 17:16:01 -0700861 try {
Jack He1f686f62017-08-17 12:11:18 -0700862 service.setForceScoAudio(forced);
Jack He798d7282017-05-09 17:16:01 -0700863 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700864 Log.e(TAG, e.toString());
Jack He798d7282017-05-09 17:16:01 -0700865 }
866 } else {
867 Log.w(TAG, "Proxy not attached to service");
868 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
869 }
870 }
871
872 /**
Jack Hec46a01e2018-05-02 19:10:56 -0700873 * Check if at least one headset's SCO audio is connected or connecting
Matthew Xief3ee3512012-02-16 16:57:18 -0800874 *
875 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
876 *
Jack Hec46a01e2018-05-02 19:10:56 -0700877 * @return true if at least one device's SCO audio is connected or connecting, false otherwise
878 * or on error
Matthew Xief3ee3512012-02-16 16:57:18 -0800879 * @hide
880 */
881 public boolean isAudioOn() {
Matthew Xief8035a72012-10-09 22:10:37 -0700882 if (VDBG) log("isAudioOn()");
Jack He1f686f62017-08-17 12:11:18 -0700883 final IBluetoothHeadset service = mService;
884 if (service != null && isEnabled()) {
Matthew Xief3ee3512012-02-16 16:57:18 -0800885 try {
Jack He1f686f62017-08-17 12:11:18 -0700886 return service.isAudioOn();
Matthew Xief3ee3512012-02-16 16:57:18 -0800887 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700888 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Matthew Xief3ee3512012-02-16 16:57:18 -0800889 }
890 }
Jack He1f686f62017-08-17 12:11:18 -0700891 if (service == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xief3ee3512012-02-16 16:57:18 -0800892 return false;
893
894 }
895
896 /**
Jack Hec46a01e2018-05-02 19:10:56 -0700897 * Initiates a connection of headset audio to the current active device
Matthew Xief3ee3512012-02-16 16:57:18 -0800898 *
Jack Hec46a01e2018-05-02 19:10:56 -0700899 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
900 * If this function returns true, this intent will be broadcasted with
901 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
902 *
903 * <p> {@link #EXTRA_STATE} will transition from
904 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
905 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
906 * in case of failure to establish the audio connection.
907 *
908 * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true
909 * before calling this method
910 *
911 * @return false if there was some error such as there is no active headset
Matthew Xief3ee3512012-02-16 16:57:18 -0800912 * @hide
913 */
Mathew Inwood7d543892018-08-01 15:07:20 +0100914 @UnsupportedAppUsage
Matthew Xief3ee3512012-02-16 16:57:18 -0800915 public boolean connectAudio() {
Jack He1f686f62017-08-17 12:11:18 -0700916 final IBluetoothHeadset service = mService;
917 if (service != null && isEnabled()) {
Matthew Xief3ee3512012-02-16 16:57:18 -0800918 try {
Jack He1f686f62017-08-17 12:11:18 -0700919 return service.connectAudio();
Matthew Xief3ee3512012-02-16 16:57:18 -0800920 } catch (RemoteException e) {
921 Log.e(TAG, e.toString());
922 }
923 } else {
924 Log.w(TAG, "Proxy not attached to service");
925 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
926 }
927 return false;
928 }
929
930 /**
Jack Hec46a01e2018-05-02 19:10:56 -0700931 * Initiates a disconnection of HFP SCO audio.
932 * Tear down voice recognition or virtual voice call if any.
Matthew Xief3ee3512012-02-16 16:57:18 -0800933 *
Jack Hec46a01e2018-05-02 19:10:56 -0700934 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
935 * If this function returns true, this intent will be broadcasted with
936 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
937 *
938 * @return false if audio is not connected, or on error, true otherwise
Matthew Xief3ee3512012-02-16 16:57:18 -0800939 * @hide
940 */
Mathew Inwood7d543892018-08-01 15:07:20 +0100941 @UnsupportedAppUsage
Matthew Xief3ee3512012-02-16 16:57:18 -0800942 public boolean disconnectAudio() {
Jack He1f686f62017-08-17 12:11:18 -0700943 final IBluetoothHeadset service = mService;
944 if (service != null && isEnabled()) {
Matthew Xief3ee3512012-02-16 16:57:18 -0800945 try {
Jack He1f686f62017-08-17 12:11:18 -0700946 return service.disconnectAudio();
Matthew Xief3ee3512012-02-16 16:57:18 -0800947 } catch (RemoteException e) {
948 Log.e(TAG, e.toString());
949 }
950 } else {
951 Log.w(TAG, "Proxy not attached to service");
952 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
953 }
954 return false;
955 }
956
957 /**
Jack Hec46a01e2018-05-02 19:10:56 -0700958 * Initiates a SCO channel connection as a virtual voice call to the current active device
959 * Active handsfree device will be notified of incoming call and connected call.
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700960 *
Jack Hec46a01e2018-05-02 19:10:56 -0700961 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
962 * If this function returns true, this intent will be broadcasted with
963 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
964 *
965 * <p> {@link #EXTRA_STATE} will transition from
966 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
967 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
968 * in case of failure to establish the audio connection.
969 *
970 * @return true if successful, false if one of the following case applies
971 * - SCO audio is not idle (connecting or connected)
972 * - virtual call has already started
973 * - there is no active device
974 * - a Telecom managed call is going on
975 * - binder is dead or Bluetooth is disabled or other error
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700976 * @hide
977 */
Jack Hec46a01e2018-05-02 19:10:56 -0700978 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood7d543892018-08-01 15:07:20 +0100979 @UnsupportedAppUsage
Jack Hec46a01e2018-05-02 19:10:56 -0700980 public boolean startScoUsingVirtualVoiceCall() {
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800981 if (DBG) log("startScoUsingVirtualVoiceCall()");
Jack He1f686f62017-08-17 12:11:18 -0700982 final IBluetoothHeadset service = mService;
Jack Hec46a01e2018-05-02 19:10:56 -0700983 if (service != null && isEnabled()) {
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700984 try {
Jack Hec46a01e2018-05-02 19:10:56 -0700985 return service.startScoUsingVirtualVoiceCall();
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700986 } catch (RemoteException e) {
987 Log.e(TAG, e.toString());
988 }
989 } else {
990 Log.w(TAG, "Proxy not attached to service");
991 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
992 }
993 return false;
994 }
995
996 /**
Jack Hec46a01e2018-05-02 19:10:56 -0700997 * Terminates an ongoing SCO connection and the associated virtual call.
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700998 *
Jack Hec46a01e2018-05-02 19:10:56 -0700999 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
1000 * If this function returns true, this intent will be broadcasted with
1001 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
1002 *
1003 * @return true if successful, false if one of the following case applies
1004 * - virtual voice call is not started or has ended
1005 * - binder is dead or Bluetooth is disabled or other error
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -07001006 * @hide
1007 */
Jack Hec46a01e2018-05-02 19:10:56 -07001008 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood7d543892018-08-01 15:07:20 +01001009 @UnsupportedAppUsage
Jack Hec46a01e2018-05-02 19:10:56 -07001010 public boolean stopScoUsingVirtualVoiceCall() {
Jaikumar Ganesheea6d262011-01-24 13:55:27 -08001011 if (DBG) log("stopScoUsingVirtualVoiceCall()");
Jack He1f686f62017-08-17 12:11:18 -07001012 final IBluetoothHeadset service = mService;
Jack Hec46a01e2018-05-02 19:10:56 -07001013 if (service != null && isEnabled()) {
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -07001014 try {
Jack Hec46a01e2018-05-02 19:10:56 -07001015 return service.stopScoUsingVirtualVoiceCall();
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -07001016 } catch (RemoteException e) {
1017 Log.e(TAG, e.toString());
1018 }
1019 } else {
1020 Log.w(TAG, "Proxy not attached to service");
1021 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1022 }
1023 return false;
1024 }
1025
Matthew Xief3ee3512012-02-16 16:57:18 -08001026 /**
1027 * Notify Headset of phone state change.
1028 * This is a backdoor for phone app to call BluetoothHeadset since
1029 * there is currently not a good way to get precise call state change outside
1030 * of phone app.
1031 *
1032 * @hide
1033 */
Mathew Inwood049f0f52020-11-04 09:29:36 +00001034 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Matthew Xief3ee3512012-02-16 16:57:18 -08001035 public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
Benson Lied8d3392018-07-17 18:19:59 +08001036 int type, String name) {
Jack He1f686f62017-08-17 12:11:18 -07001037 final IBluetoothHeadset service = mService;
1038 if (service != null && isEnabled()) {
Matthew Xief3ee3512012-02-16 16:57:18 -08001039 try {
Benson Lied8d3392018-07-17 18:19:59 +08001040 service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
Matthew Xief3ee3512012-02-16 16:57:18 -08001041 } catch (RemoteException e) {
1042 Log.e(TAG, e.toString());
1043 }
1044 } else {
1045 Log.w(TAG, "Proxy not attached to service");
1046 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1047 }
1048 }
1049
1050 /**
Matthew Xief3ee3512012-02-16 16:57:18 -08001051 * Send Headset of CLCC response
1052 *
1053 * @hide
1054 */
1055 public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
Jack He910201b2017-08-22 16:06:54 -07001056 String number, int type) {
Jack He1f686f62017-08-17 12:11:18 -07001057 final IBluetoothHeadset service = mService;
1058 if (service != null && isEnabled()) {
Matthew Xief3ee3512012-02-16 16:57:18 -08001059 try {
Jack He1f686f62017-08-17 12:11:18 -07001060 service.clccResponse(index, direction, status, mode, mpty, number, type);
Matthew Xief3ee3512012-02-16 16:57:18 -08001061 } catch (RemoteException e) {
1062 Log.e(TAG, e.toString());
1063 }
1064 } else {
1065 Log.w(TAG, "Proxy not attached to service");
1066 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1067 }
1068 }
1069
Edward Jee7c81f1f2013-08-16 04:07:49 -07001070 /**
1071 * Sends a vendor-specific unsolicited result code to the headset.
1072 *
Jack He910201b2017-08-22 16:06:54 -07001073 * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code
1074 * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the
1075 * string <code>"+ANDROID: 0"</code> will be sent.
Edward Jee7c81f1f2013-08-16 04:07:49 -07001076 *
Ying Wang29d93892013-08-26 17:48:22 -07001077 * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
Edward Jee7c81f1f2013-08-16 04:07:49 -07001078 *
1079 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1080 *
1081 * @param device Bluetooth headset.
1082 * @param command A vendor-specific command.
1083 * @param arg The argument that will be attached to the command.
1084 * @return {@code false} if there is no headset connected, or if the command is not an allowed
Jack He910201b2017-08-22 16:06:54 -07001085 * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
Edward Jee7c81f1f2013-08-16 04:07:49 -07001086 * @throws IllegalArgumentException if {@code command} is {@code null}.
1087 */
1088 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
1089 String arg) {
1090 if (DBG) {
1091 log("sendVendorSpecificResultCode()");
1092 }
1093 if (command == null) {
1094 throw new IllegalArgumentException("command is null");
1095 }
Jack He1f686f62017-08-17 12:11:18 -07001096 final IBluetoothHeadset service = mService;
1097 if (service != null && isEnabled() && isValidDevice(device)) {
Edward Jee7c81f1f2013-08-16 04:07:49 -07001098 try {
Jack He1f686f62017-08-17 12:11:18 -07001099 return service.sendVendorSpecificResultCode(device, command, arg);
Edward Jee7c81f1f2013-08-16 04:07:49 -07001100 } catch (RemoteException e) {
1101 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1102 }
1103 }
Jack He1f686f62017-08-17 12:11:18 -07001104 if (service == null) {
Edward Jee7c81f1f2013-08-16 04:07:49 -07001105 Log.w(TAG, "Proxy not attached to service");
1106 }
1107 return false;
1108 }
1109
Mudumba Ananth80bf6282014-04-27 13:11:00 -07001110 /**
Jack He889d2342018-01-03 12:13:26 -08001111 * Select a connected device as active.
1112 *
1113 * The active device selection is per profile. An active device's
1114 * purpose is profile-specific. For example, in HFP and HSP profiles,
1115 * it is the device used for phone call audio. If a remote device is not
1116 * connected, it cannot be selected as active.
1117 *
1118 * <p> This API returns false in scenarios like the profile on the
1119 * device is not connected or Bluetooth is not turned on.
1120 * When this API returns true, it is guaranteed that the
1121 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
1122 * with the active device.
1123 *
1124 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
1125 * permission.
1126 *
1127 * @param device Remote Bluetooth Device, could be null if phone call audio should not be
1128 * streamed to a headset
1129 * @return false on immediate error, true otherwise
1130 * @hide
1131 */
1132 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwoodb426f902021-01-06 12:05:47 +00001133 @UnsupportedAppUsage(trackingBug = 171933273)
Jack He889d2342018-01-03 12:13:26 -08001134 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
1135 if (DBG) {
1136 Log.d(TAG, "setActiveDevice: " + device);
1137 }
1138 final IBluetoothHeadset service = mService;
1139 if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
1140 try {
1141 return service.setActiveDevice(device);
1142 } catch (RemoteException e) {
1143 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1144 }
1145 }
1146 if (service == null) {
1147 Log.w(TAG, "Proxy not attached to service");
1148 }
1149 return false;
1150 }
1151
1152 /**
1153 * Get the connected device that is active.
1154 *
Jack He889d2342018-01-03 12:13:26 -08001155 * @return the connected device that is active or null if no device
1156 * is active.
1157 * @hide
1158 */
Mathew Inwoodb426f902021-01-06 12:05:47 +00001159 @UnsupportedAppUsage(trackingBug = 171933273)
Rahul Sabnisd9798612019-12-04 14:21:10 -08001160 @Nullable
Rahul Sabnisc228ce22020-03-18 17:46:33 -07001161 @RequiresPermission(Manifest.permission.BLUETOOTH)
Jack He889d2342018-01-03 12:13:26 -08001162 public BluetoothDevice getActiveDevice() {
1163 if (VDBG) {
1164 Log.d(TAG, "getActiveDevice");
1165 }
1166 final IBluetoothHeadset service = mService;
1167 if (service != null && isEnabled()) {
1168 try {
1169 return service.getActiveDevice();
1170 } catch (RemoteException e) {
1171 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1172 }
1173 }
1174 if (service == null) {
1175 Log.w(TAG, "Proxy not attached to service");
1176 }
1177 return null;
1178 }
1179
1180 /**
Jack Hec5fde732018-01-05 17:17:06 -08001181 * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an
1182 * active connection.
Jack Hed8d204d2016-11-17 16:19:43 -08001183 *
Jack Hec5fde732018-01-05 17:17:06 -08001184 * @return true if in-band ringing is enabled, false if in-band ringing is disabled
1185 * @hide
1186 */
1187 @RequiresPermission(android.Manifest.permission.BLUETOOTH)
1188 public boolean isInbandRingingEnabled() {
1189 if (DBG) {
1190 log("isInbandRingingEnabled()");
1191 }
1192 final IBluetoothHeadset service = mService;
1193 if (service != null && isEnabled()) {
1194 try {
1195 return service.isInbandRingingEnabled();
1196 } catch (RemoteException e) {
1197 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1198 }
1199 }
1200 if (service == null) {
1201 Log.w(TAG, "Proxy not attached to service");
1202 }
1203 return false;
1204 }
1205
1206 /**
1207 * Check if in-band ringing is supported for this platform.
1208 *
1209 * @return true if in-band ringing is supported, false if in-band ringing is not supported
Jack Hed8d204d2016-11-17 16:19:43 -08001210 * @hide
1211 */
1212 public static boolean isInbandRingingSupported(Context context) {
1213 return context.getResources().getBoolean(
1214 com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
1215 }
1216
Jack He9e045d22017-08-22 21:21:23 -07001217 private final IBluetoothProfileServiceConnection mConnection =
1218 new IBluetoothProfileServiceConnection.Stub() {
Benjamin Franz3362fef2014-11-12 15:57:54 +00001219 @Override
The Android Open Source Project33897762009-03-03 19:31:44 -08001220 public void onServiceConnected(ComponentName className, IBinder service) {
1221 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey73458a82016-11-04 11:23:46 -06001222 mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
Benjamin Franz3362fef2014-11-12 15:57:54 +00001223 mHandler.sendMessage(mHandler.obtainMessage(
1224 MESSAGE_HEADSET_SERVICE_CONNECTED));
The Android Open Source Project33897762009-03-03 19:31:44 -08001225 }
Jack He910201b2017-08-22 16:06:54 -07001226
Benjamin Franz3362fef2014-11-12 15:57:54 +00001227 @Override
The Android Open Source Project33897762009-03-03 19:31:44 -08001228 public void onServiceDisconnected(ComponentName className) {
1229 if (DBG) Log.d(TAG, "Proxy object disconnected");
Ugo Yu1652b942019-03-26 21:38:08 +08001230 doUnbind();
Benjamin Franz3362fef2014-11-12 15:57:54 +00001231 mHandler.sendMessage(mHandler.obtainMessage(
1232 MESSAGE_HEADSET_SERVICE_DISCONNECTED));
The Android Open Source Project33897762009-03-03 19:31:44 -08001233 }
1234 };
The Android Open Source Project0047a0f2009-03-05 20:00:43 -08001235
Mathew Inwood7d543892018-08-01 15:07:20 +01001236 @UnsupportedAppUsage
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001237 private boolean isEnabled() {
Jack He1f686f62017-08-17 12:11:18 -07001238 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001239 }
1240
Jaikumar Ganesh2fbe8f42011-04-06 11:09:30 -07001241 private boolean isDisabled() {
Jack He1f686f62017-08-17 12:11:18 -07001242 return mAdapter.getState() == BluetoothAdapter.STATE_OFF;
Jaikumar Ganesh2fbe8f42011-04-06 11:09:30 -07001243 }
1244
Jack He1f686f62017-08-17 12:11:18 -07001245 private static boolean isValidDevice(BluetoothDevice device) {
1246 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001247 }
1248
The Android Open Source Project0047a0f2009-03-05 20:00:43 -08001249 private static void log(String msg) {
1250 Log.d(TAG, msg);
1251 }
Benjamin Franz3362fef2014-11-12 15:57:54 +00001252
1253 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
1254 @Override
1255 public void handleMessage(Message msg) {
1256 switch (msg.what) {
1257 case MESSAGE_HEADSET_SERVICE_CONNECTED: {
1258 if (mServiceListener != null) {
1259 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
1260 BluetoothHeadset.this);
1261 }
1262 break;
1263 }
1264 case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
1265 if (mServiceListener != null) {
1266 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
1267 }
Benjamin Franz3362fef2014-11-12 15:57:54 +00001268 break;
1269 }
1270 }
1271 }
1272 };
The Android Open Source Project33897762009-03-03 19:31:44 -08001273}