blob: ea3831a869fc5b06ff5ca4e776a8731d64ce570a [file] [log] [blame]
The Android Open Source Project9066cfe2009-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 He896e1292018-05-02 19:10:56 -070019import android.Manifest;
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -080020import android.annotation.NonNull;
Jack He05f4bc42018-01-03 12:13:26 -080021import android.annotation.Nullable;
Selim Gurun4029fa62017-10-17 17:01:38 -070022import android.annotation.RequiresPermission;
Nick Pelly005b2282009-09-10 10:21:56 -070023import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
Selim Gurun4029fa62017-10-17 17:01:38 -070025import android.annotation.SystemApi;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010026import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.ComponentName;
28import android.content.Context;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060029import android.os.Binder;
Benjamin Franze8b98922014-11-12 15:57:54 +000030import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.IBinder;
Benjamin Franze8b98922014-11-12 15:57:54 +000032import android.os.Looper;
33import android.os.Message;
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070034import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.util.Log;
36
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070037import java.util.ArrayList;
38import java.util.List;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040/**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 * Public API for controlling the Bluetooth Headset Service. This includes both
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070042 * Bluetooth Headset and Handsfree (v1.5) profiles.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070044 * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 * Service via IPC.
46 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070047 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
48 * the BluetoothHeadset proxy object. Use
49 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070051 * <p> Android only supports one connected Bluetooth Headset at a time.
52 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070054public final class BluetoothHeadset implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 private static final String TAG = "BluetoothHeadset";
fredc0f420372012-04-12 00:02:00 -070056 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070057 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
Nick Pelly005b2282009-09-10 10:21:56 -070059 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070060 * Intent used to broadcast the change in connection state of the Headset
61 * profile.
62 *
63 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080064 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070065 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
66 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
67 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080068 * </ul>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080069 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070070 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
71 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
72 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080073 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
74 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070075 */
76 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
77 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070078 "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070079
80 /**
81 * Intent used to broadcast the change in the Audio Connection state of the
82 * A2DP profile.
83 *
84 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080085 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070086 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
87 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
88 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080089 * </ul>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080090 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070091 * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
92 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080093 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
94 * to receive.
Nick Pelly005b2282009-09-10 10:21:56 -070095 */
96 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
97 public static final String ACTION_AUDIO_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070098 "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
Nick Pelly005b2282009-09-10 10:21:56 -070099
Jack He05f4bc42018-01-03 12:13:26 -0800100 /**
101 * Intent used to broadcast the selection of a connected device as active.
102 *
103 * <p>This intent will have one extra:
104 * <ul>
105 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
106 * be null if no device is active. </li>
107 * </ul>
108 *
109 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
110 * receive.
111 *
112 * @hide
113 */
114 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100115 @UnsupportedAppUsage
Jack He05f4bc42018-01-03 12:13:26 -0800116 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
117 "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
Jaikumar Ganeshc24dbdb2010-04-02 14:44:43 -0700118
Nick Pelly005b2282009-09-10 10:21:56 -0700119 /**
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700120 * Intent used to broadcast that the headset has posted a
121 * vendor-specific event.
122 *
123 * <p>This intent will have 4 extras and 1 category.
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800124 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700125 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
126 * </li>
127 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
128 * specific command </li>
129 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
130 * command type which can be one of {@link #AT_CMD_TYPE_READ},
131 * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
132 * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
133 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
134 * arguments. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800135 * </ul>
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700136 *
Jack Hea355e5e2017-08-22 16:06:54 -0700137 * <p> The category is the Company ID of the vendor defining the
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700138 * vendor-specific command. {@link BluetoothAssignedNumbers}
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700139 *
140 * For example, for Plantronics specific events
141 * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
142 *
143 * <p> For example, an AT+XEVENT=foo,3 will get translated into
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800144 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700145 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
146 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
147 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800148 * </ul>
149 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
150 * to receive.
Herb Jellineka4733942010-08-10 13:17:43 -0700151 */
152 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
153 public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
154 "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
155
156 /**
157 * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
158 * intents that contains the name of the vendor-specific command.
159 */
160 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
161 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
162
163 /**
164 * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700165 * intents that contains the AT command type of the vendor-specific command.
Herb Jellineka4733942010-08-10 13:17:43 -0700166 */
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700167 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
168 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
169
170 /**
171 * AT command type READ used with
172 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
173 * For example, AT+VGM?. There are no arguments for this command type.
174 */
175 public static final int AT_CMD_TYPE_READ = 0;
176
177 /**
178 * AT command type TEST used with
179 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
180 * For example, AT+VGM=?. There are no arguments for this command type.
181 */
182 public static final int AT_CMD_TYPE_TEST = 1;
183
184 /**
185 * AT command type SET used with
186 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
187 * For example, AT+VGM=<args>.
188 */
189 public static final int AT_CMD_TYPE_SET = 2;
190
191 /**
192 * AT command type BASIC used with
193 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
194 * For example, ATD. Single character commands and everything following the
195 * character are arguments.
196 */
197 public static final int AT_CMD_TYPE_BASIC = 3;
198
199 /**
200 * AT command type ACTION used with
201 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
202 * For example, AT+CHUP. There are no arguments for action commands.
203 */
204 public static final int AT_CMD_TYPE_ACTION = 4;
Herb Jellineka4733942010-08-10 13:17:43 -0700205
206 /**
207 * A Parcelable String array extra field in
208 * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
209 * the arguments to the vendor-specific command.
210 */
211 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
212 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
213
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700214 /**
215 * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
216 * for the companyId
217 */
Jack Hea355e5e2017-08-22 16:06:54 -0700218 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700219 "android.bluetooth.headset.intent.category.companyid";
220
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700221 /**
Edward Jee922d41b2013-08-16 04:07:49 -0700222 * A vendor-specific command for unsolicited result code.
223 */
224 public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
225
226 /**
Jack He0fcbba22017-06-20 17:07:40 -0700227 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700228 *
Jack He0fcbba22017-06-20 17:07:40 -0700229 * @hide
230 */
Jack He679d0bc2017-06-20 17:09:47 -0700231 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
232
233 /**
234 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700235 *
Jack He679d0bc2017-06-20 17:09:47 -0700236 * @hide
237 */
238 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
239
240 /**
241 * Battery level indicator associated with
242 * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
Jack Hea355e5e2017-08-22 16:06:54 -0700243 *
Jack He679d0bc2017-06-20 17:09:47 -0700244 * @hide
245 */
246 public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
247
248 /**
249 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700250 *
Jack He679d0bc2017-06-20 17:09:47 -0700251 * @hide
252 */
Jack He0fcbba22017-06-20 17:07:40 -0700253 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
254
255 /**
256 * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
Jack Hea355e5e2017-08-22 16:06:54 -0700257 *
Jack He0fcbba22017-06-20 17:07:40 -0700258 * @hide
259 */
260 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
261
262 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800263 * Headset state when SCO audio is not connected.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700264 * This state can be one of
265 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
266 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
267 */
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800268 public static final int STATE_AUDIO_DISCONNECTED = 10;
Herb Jellineka4733942010-08-10 13:17:43 -0700269
270 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800271 * Headset state when SCO audio is connecting.
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700272 * This state can be one of
273 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
274 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700275 */
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800276 public static final int STATE_AUDIO_CONNECTING = 11;
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700277
278 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800279 * Headset state when SCO audio is connected.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700280 * This state can be one of
281 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
282 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Nick Pelly005b2282009-09-10 10:21:56 -0700283 */
Chienyuana669d1a2019-05-29 10:29:30 +0800284 public static final int STATE_AUDIO_CONNECTED = 12;
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800285
286 /**
287 * Intent used to broadcast the headset's indicator status
288 *
289 * <p>This intent will have 3 extras:
290 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700291 * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
292 * is supported by the headset ( as indicated by AT+BIND command in the SLC
293 * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
294 * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
295 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800296 * </ul>
Jack He73795442017-06-22 12:56:54 -0700297 * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
Jack Hea355e5e2017-08-22 16:06:54 -0700298 * are given an assigned number. Below shows the assigned number of Indicator added so far
Jack He73795442017-06-22 12:56:54 -0700299 * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
300 * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
301 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
Jack Hea355e5e2017-08-22 16:06:54 -0700302 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800303 * @hide
304 */
305 public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
306 "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
307
308 /**
Jack He73795442017-06-22 12:56:54 -0700309 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
310 * intents that contains the assigned number of the headset indicator as defined by
311 * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
Jack Hea355e5e2017-08-22 16:06:54 -0700312 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800313 * @hide
314 */
315 public static final String EXTRA_HF_INDICATORS_IND_ID =
316 "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
317
318 /**
Jack He73795442017-06-22 12:56:54 -0700319 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800320 * intents that contains the value of the Headset indicator that is being sent.
Jack Hea355e5e2017-08-22 16:06:54 -0700321 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800322 * @hide
323 */
324 public static final String EXTRA_HF_INDICATORS_IND_VALUE =
325 "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
326
Benjamin Franze8b98922014-11-12 15:57:54 +0000327 private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
328 private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700329
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700330 private Context mContext;
331 private ServiceListener mServiceListener;
Jack He16eeac32017-08-17 12:11:18 -0700332 private volatile IBluetoothHeadset mService;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700333 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
Jack He2992cd02017-08-22 21:21:23 -0700335 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc0f420372012-04-12 00:02:00 -0700336 new IBluetoothStateChangeCallback.Stub() {
337 public void onBluetoothStateChange(boolean up) {
338 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
339 if (!up) {
Benjamin Franze8b98922014-11-12 15:57:54 +0000340 doUnbind();
fredc0f420372012-04-12 00:02:00 -0700341 } else {
Ugo Yud0115462019-03-26 21:38:08 +0800342 doBind();
fredc0f420372012-04-12 00:02:00 -0700343 }
344 }
Jack Hea355e5e2017-08-22 16:06:54 -0700345 };
fredc0f420372012-04-12 00:02:00 -0700346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 /**
348 * Create a BluetoothHeadset proxy object.
349 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700350 /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 mContext = context;
352 mServiceListener = l;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700353 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700354
355 IBluetoothManager mgr = mAdapter.getBluetoothManager();
356 if (mgr != null) {
357 try {
358 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
359 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700360 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700361 }
362 }
363
Dianne Hackborn221ea892013-08-04 16:50:16 -0700364 doBind();
365 }
366
Ugo Yud0115462019-03-26 21:38:08 +0800367 private boolean doBind() {
368 synchronized (mConnection) {
369 if (mService == null) {
370 if (VDBG) Log.d(TAG, "Binding service...");
371 try {
372 return mAdapter.getBluetoothManager().bindBluetoothProfileService(
373 BluetoothProfile.HEADSET, mConnection);
374 } catch (RemoteException e) {
375 Log.e(TAG, "Unable to bind HeadsetService", e);
376 }
377 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 }
Benjamin Franze8b98922014-11-12 15:57:54 +0000379 return false;
380 }
381
Ugo Yud0115462019-03-26 21:38:08 +0800382 private void doUnbind() {
Benjamin Franze8b98922014-11-12 15:57:54 +0000383 synchronized (mConnection) {
384 if (mService != null) {
Ugo Yud0115462019-03-26 21:38:08 +0800385 if (VDBG) Log.d(TAG, "Unbinding service...");
Benjamin Franze8b98922014-11-12 15:57:54 +0000386 try {
387 mAdapter.getBluetoothManager().unbindBluetoothProfileService(
388 BluetoothProfile.HEADSET, mConnection);
389 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700390 Log.e(TAG, "Unable to unbind HeadsetService", e);
Ugo Yud0115462019-03-26 21:38:08 +0800391 } finally {
392 mService = null;
Benjamin Franze8b98922014-11-12 15:57:54 +0000393 }
394 }
395 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 }
397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 /**
399 * Close the connection to the backing service.
400 * Other public functions of BluetoothHeadset will return default error
401 * results once close() has been called. Multiple invocations of close()
402 * are ok.
403 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100404 @UnsupportedAppUsage
Matthew Xie13450df2012-03-22 17:18:37 -0700405 /*package*/ void close() {
Matthew Xie563e4142012-10-09 22:10:37 -0700406 if (VDBG) log("close()");
fredc0f420372012-04-12 00:02:00 -0700407
408 IBluetoothManager mgr = mAdapter.getBluetoothManager();
409 if (mgr != null) {
410 try {
411 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
Ugo Yud0115462019-03-26 21:38:08 +0800412 } catch (RemoteException re) {
413 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700414 }
415 }
Benjamin Franzc88b6bd2014-12-16 15:33:03 +0000416 mServiceListener = null;
Benjamin Franze8b98922014-11-12 15:57:54 +0000417 doUnbind();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 }
419
420 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700421 * Initiate connection to a profile of the remote bluetooth device.
422 *
423 * <p> Currently, the system supports only 1 connection to the
424 * headset/handsfree profile. The API will automatically disconnect connected
425 * devices before connecting.
426 *
427 * <p> This API returns false in scenarios like the profile on the
428 * device is already connected or Bluetooth is not turned on.
429 * When this API returns true, it is guaranteed that
430 * connection state intent for the profile will be broadcasted with
431 * the state. Users can get the connection state of the profile
432 * from this intent.
433 *
434 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
435 * permission.
436 *
437 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700438 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700439 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 */
Selim Gurun4029fa62017-10-17 17:01:38 -0700441 @SystemApi
442 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700443 public boolean connect(BluetoothDevice device) {
444 if (DBG) log("connect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700445 final IBluetoothHeadset service = mService;
446 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 try {
Jack He16eeac32017-08-17 12:11:18 -0700448 return service.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700449 } catch (RemoteException e) {
450 Log.e(TAG, Log.getStackTraceString(new Throwable()));
451 return false;
452 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
Jack He16eeac32017-08-17 12:11:18 -0700454 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 return false;
456 }
457
458 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700459 * Initiate disconnection from a profile
460 *
461 * <p> This API will return false in scenarios like the profile on the
462 * Bluetooth device is not in connected state etc. When this API returns,
463 * true, it is guaranteed that the connection state change
464 * intent will be broadcasted with the state. Users can get the
465 * disconnection state of the profile from this intent.
466 *
467 * <p> If the disconnection is initiated by a remote device, the state
468 * will transition from {@link #STATE_CONNECTED} to
469 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
470 * host (local) device the state will transition from
471 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
472 * state {@link #STATE_DISCONNECTED}. The transition to
473 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
474 * two scenarios.
475 *
476 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
477 * permission.
478 *
479 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700480 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700481 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 */
Selim Gurun4029fa62017-10-17 17:01:38 -0700483 @SystemApi
484 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700485 public boolean disconnect(BluetoothDevice device) {
486 if (DBG) log("disconnect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700487 final IBluetoothHeadset service = mService;
488 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 try {
Jack He16eeac32017-08-17 12:11:18 -0700490 return service.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700491 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700492 Log.e(TAG, Log.getStackTraceString(new Throwable()));
493 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700494 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 }
Jack He16eeac32017-08-17 12:11:18 -0700496 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 return false;
498 }
499
500 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700501 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 */
Jack He2992cd02017-08-22 21:21:23 -0700503 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700504 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700505 if (VDBG) log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700506 final IBluetoothHeadset service = mService;
507 if (service != null && isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 try {
Jack He16eeac32017-08-17 12:11:18 -0700509 return service.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700510 } catch (RemoteException e) {
511 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700512 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700513 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 }
Jack He16eeac32017-08-17 12:11:18 -0700515 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700516 return new ArrayList<BluetoothDevice>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518
519 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700520 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 */
Jack He2992cd02017-08-22 21:21:23 -0700522 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700523 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700524 if (VDBG) log("getDevicesMatchingStates()");
Jack He16eeac32017-08-17 12:11:18 -0700525 final IBluetoothHeadset service = mService;
526 if (service != null && isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 try {
Jack He16eeac32017-08-17 12:11:18 -0700528 return service.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700529 } catch (RemoteException e) {
530 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700531 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700532 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 }
Jack He16eeac32017-08-17 12:11:18 -0700534 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700535 return new ArrayList<BluetoothDevice>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 }
537
538 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700539 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 */
Jack He2992cd02017-08-22 21:21:23 -0700541 @Override
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700542 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700543 if (VDBG) log("getConnectionState(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700544 final IBluetoothHeadset service = mService;
545 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 try {
Jack He16eeac32017-08-17 12:11:18 -0700547 return service.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700548 } catch (RemoteException e) {
549 Log.e(TAG, Log.getStackTraceString(new Throwable()));
550 return BluetoothProfile.STATE_DISCONNECTED;
551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
Jack He16eeac32017-08-17 12:11:18 -0700553 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700554 return BluetoothProfile.STATE_DISCONNECTED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 }
556
557 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700558 * Set priority of the profile
559 *
560 * <p> The device should already be paired.
Selim Gurun17a26c72018-01-09 14:35:19 -0800561 * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
562 * {@link BluetoothProfile#PRIORITY_OFF},
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700563 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700564 * @param device Paired bluetooth device
565 * @param priority
566 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700567 * @hide
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800568 * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 */
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800570 @Deprecated
Selim Gurun4029fa62017-10-17 17:01:38 -0700571 @SystemApi
572 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Nick Pellybd022f42009-08-14 18:33:38 -0700573 public boolean setPriority(BluetoothDevice device, int priority) {
574 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800575 return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
576 }
577
578 /**
579 * Set connection policy of the profile
580 *
581 * <p> The device should already be paired.
582 * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
583 * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
584 *
585 * @param device Paired bluetooth device
586 * @param connectionPolicy is the connection policy to set to for this profile
587 * @return true if connectionPolicy is set, false on error
588 * @hide
589 */
590 @SystemApi
591 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
592 public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
593 @ConnectionPolicy int connectionPolicy) {
594 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
Jack He16eeac32017-08-17 12:11:18 -0700595 final IBluetoothHeadset service = mService;
596 if (service != null && isEnabled() && isValidDevice(device)) {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800597 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
598 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
Jack Hea355e5e2017-08-22 16:06:54 -0700599 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700600 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 try {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800602 return service.setConnectionPolicy(device, connectionPolicy);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700603 } catch (RemoteException e) {
604 Log.e(TAG, Log.getStackTraceString(new Throwable()));
605 return false;
606 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 }
Jack He16eeac32017-08-17 12:11:18 -0700608 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 return false;
610 }
611
612 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700613 * Get the priority of the profile.
614 *
615 * <p> The priority can be any of:
616 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
617 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
618 *
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700619 * @param device Bluetooth device
620 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700621 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100623 @UnsupportedAppUsage
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800624 @RequiresPermission(Manifest.permission.BLUETOOTH)
Nick Pellybd022f42009-08-14 18:33:38 -0700625 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700626 if (VDBG) log("getPriority(" + device + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800627 return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
628 }
629
630 /**
631 * Get the connection policy of the profile.
632 *
633 * <p> The connection policy can be any of:
634 * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
635 * {@link #CONNECTION_POLICY_UNKNOWN}
636 *
637 * @param device Bluetooth device
638 * @return connection policy of the device
639 * @hide
640 */
641 @SystemApi
642 @RequiresPermission(Manifest.permission.BLUETOOTH)
643 public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
644 if (VDBG) log("getConnectionPolicy(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700645 final IBluetoothHeadset service = mService;
646 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 try {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800648 return service.getConnectionPolicy(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700649 } catch (RemoteException e) {
650 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800651 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700652 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 }
Jack He16eeac32017-08-17 12:11:18 -0700654 if (service == null) Log.w(TAG, "Proxy not attached to service");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800655 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700656 }
657
658 /**
659 * Start Bluetooth voice recognition. This methods sends the voice
660 * recognition AT command to the headset and establishes the
661 * audio connection.
662 *
663 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800664 * If this function returns true, this intent will be broadcasted with
665 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700666 *
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800667 * <p> {@link #EXTRA_STATE} will transition from
668 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
669 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
670 * in case of failure to establish the audio connection.
671 *
672 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700673 *
674 * @param device Bluetooth headset
Jack He896e1292018-05-02 19:10:56 -0700675 * @return false if there is no headset connected, or the connected headset doesn't support
676 * voice recognition, or voice recognition is already started, or audio channel is occupied,
677 * or on error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700678 */
679 public boolean startVoiceRecognition(BluetoothDevice device) {
680 if (DBG) log("startVoiceRecognition()");
Jack He16eeac32017-08-17 12:11:18 -0700681 final IBluetoothHeadset service = mService;
682 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700683 try {
Jack He16eeac32017-08-17 12:11:18 -0700684 return service.startVoiceRecognition(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700685 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700686 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700687 }
688 }
Jack He16eeac32017-08-17 12:11:18 -0700689 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700690 return false;
691 }
692
693 /**
694 * Stop Bluetooth Voice Recognition mode, and shut down the
695 * Bluetooth audio path.
696 *
Jack He896e1292018-05-02 19:10:56 -0700697 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
698 * If this function returns true, this intent will be broadcasted with
699 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
700 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800701 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700702 *
703 * @param device Bluetooth headset
Jack He896e1292018-05-02 19:10:56 -0700704 * @return false if there is no headset connected, or voice recognition has not started,
705 * or voice recognition has ended on this headset, or on error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700706 */
707 public boolean stopVoiceRecognition(BluetoothDevice device) {
708 if (DBG) log("stopVoiceRecognition()");
Jack He16eeac32017-08-17 12:11:18 -0700709 final IBluetoothHeadset service = mService;
710 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700711 try {
Jack He16eeac32017-08-17 12:11:18 -0700712 return service.stopVoiceRecognition(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700713 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700714 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700715 }
716 }
Jack He16eeac32017-08-17 12:11:18 -0700717 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700718 return false;
719 }
720
721 /**
722 * Check if Bluetooth SCO audio is connected.
723 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800724 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700725 *
726 * @param device Bluetooth headset
Jack Hea355e5e2017-08-22 16:06:54 -0700727 * @return true if SCO is connected, false otherwise or on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700728 */
729 public boolean isAudioConnected(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700730 if (VDBG) log("isAudioConnected()");
Jack He16eeac32017-08-17 12:11:18 -0700731 final IBluetoothHeadset service = mService;
732 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700733 try {
Jack He16eeac32017-08-17 12:11:18 -0700734 return service.isAudioConnected(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700735 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700736 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700737 }
738 }
Jack He16eeac32017-08-17 12:11:18 -0700739 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700740 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 }
742
743 /**
Eric Laurentd726b352010-03-17 14:59:27 -0700744 * Indicates if current platform supports voice dialing over bluetooth SCO.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700745 *
Eric Laurentd726b352010-03-17 14:59:27 -0700746 * @return true if voice dialing over bluetooth is supported, false otherwise.
747 * @hide
748 */
749 public static boolean isBluetoothVoiceDialingEnabled(Context context) {
750 return context.getResources().getBoolean(
751 com.android.internal.R.bool.config_bluetooth_sco_off_call);
752 }
753
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700754 /**
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700755 * Get the current audio state of the Headset.
756 * Note: This is an internal function and shouldn't be exposed
757 *
758 * @hide
759 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100760 @UnsupportedAppUsage
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700761 public int getAudioState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700762 if (VDBG) log("getAudioState");
Jack He16eeac32017-08-17 12:11:18 -0700763 final IBluetoothHeadset service = mService;
764 if (service != null && !isDisabled()) {
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700765 try {
Jack He16eeac32017-08-17 12:11:18 -0700766 return service.getAudioState(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700767 } catch (RemoteException e) {
768 Log.e(TAG, e.toString());
769 }
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700770 } else {
771 Log.w(TAG, "Proxy not attached to service");
772 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
773 }
774 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
775 }
776
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700777 /**
Bryce Lee0db53d92015-11-16 08:55:52 -0800778 * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
779 * audio to the HF unless explicitly told to.
780 * This method should be used in cases where the SCO channel is shared between multiple profiles
781 * and must be delegated by a source knowledgeable
782 * Note: This is an internal function and shouldn't be exposed
783 *
784 * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
Bryce Lee0db53d92015-11-16 08:55:52 -0800785 * @hide
786 */
787 public void setAudioRouteAllowed(boolean allowed) {
788 if (VDBG) log("setAudioRouteAllowed");
Jack He16eeac32017-08-17 12:11:18 -0700789 final IBluetoothHeadset service = mService;
790 if (service != null && isEnabled()) {
Bryce Lee0db53d92015-11-16 08:55:52 -0800791 try {
Jack He16eeac32017-08-17 12:11:18 -0700792 service.setAudioRouteAllowed(allowed);
Jack Hea355e5e2017-08-22 16:06:54 -0700793 } catch (RemoteException e) {
794 Log.e(TAG, e.toString());
795 }
Bryce Lee0db53d92015-11-16 08:55:52 -0800796 } else {
797 Log.w(TAG, "Proxy not attached to service");
798 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
799 }
800 }
801
802 /**
803 * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
804 * Note: This is an internal function and shouldn't be exposed
805 *
806 * @hide
807 */
808 public boolean getAudioRouteAllowed() {
809 if (VDBG) log("getAudioRouteAllowed");
Jack He16eeac32017-08-17 12:11:18 -0700810 final IBluetoothHeadset service = mService;
811 if (service != null && isEnabled()) {
Bryce Lee0db53d92015-11-16 08:55:52 -0800812 try {
Jack He16eeac32017-08-17 12:11:18 -0700813 return service.getAudioRouteAllowed();
Jack Hea355e5e2017-08-22 16:06:54 -0700814 } catch (RemoteException e) {
815 Log.e(TAG, e.toString());
816 }
Bryce Lee0db53d92015-11-16 08:55:52 -0800817 } else {
818 Log.w(TAG, "Proxy not attached to service");
819 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
820 }
821 return false;
822 }
823
824 /**
Jack He1dd1e032017-05-09 17:16:01 -0700825 * Force SCO audio to be opened regardless any other restrictions
826 *
Jack Hea355e5e2017-08-22 16:06:54 -0700827 * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio
828 * False to use SCO audio in normal manner
Jack He1dd1e032017-05-09 17:16:01 -0700829 * @hide
830 */
831 public void setForceScoAudio(boolean forced) {
832 if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
Jack He16eeac32017-08-17 12:11:18 -0700833 final IBluetoothHeadset service = mService;
834 if (service != null && isEnabled()) {
Jack He1dd1e032017-05-09 17:16:01 -0700835 try {
Jack He16eeac32017-08-17 12:11:18 -0700836 service.setForceScoAudio(forced);
Jack He1dd1e032017-05-09 17:16:01 -0700837 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700838 Log.e(TAG, e.toString());
Jack He1dd1e032017-05-09 17:16:01 -0700839 }
840 } else {
841 Log.w(TAG, "Proxy not attached to service");
842 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
843 }
844 }
845
846 /**
Jack He896e1292018-05-02 19:10:56 -0700847 * Check if at least one headset's SCO audio is connected or connecting
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800848 *
849 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
850 *
Jack He896e1292018-05-02 19:10:56 -0700851 * @return true if at least one device's SCO audio is connected or connecting, false otherwise
852 * or on error
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800853 * @hide
854 */
855 public boolean isAudioOn() {
Matthew Xie563e4142012-10-09 22:10:37 -0700856 if (VDBG) log("isAudioOn()");
Jack He16eeac32017-08-17 12:11:18 -0700857 final IBluetoothHeadset service = mService;
858 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800859 try {
Jack He16eeac32017-08-17 12:11:18 -0700860 return service.isAudioOn();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800861 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700862 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800863 }
864 }
Jack He16eeac32017-08-17 12:11:18 -0700865 if (service == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800866 return false;
867
868 }
869
870 /**
Jack He896e1292018-05-02 19:10:56 -0700871 * Initiates a connection of headset audio to the current active device
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800872 *
Jack He896e1292018-05-02 19:10:56 -0700873 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
874 * If this function returns true, this intent will be broadcasted with
875 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
876 *
877 * <p> {@link #EXTRA_STATE} will transition from
878 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
879 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
880 * in case of failure to establish the audio connection.
881 *
882 * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true
883 * before calling this method
884 *
885 * @return false if there was some error such as there is no active headset
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800886 * @hide
887 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100888 @UnsupportedAppUsage
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800889 public boolean connectAudio() {
Jack He16eeac32017-08-17 12:11:18 -0700890 final IBluetoothHeadset service = mService;
891 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800892 try {
Jack He16eeac32017-08-17 12:11:18 -0700893 return service.connectAudio();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800894 } catch (RemoteException e) {
895 Log.e(TAG, e.toString());
896 }
897 } else {
898 Log.w(TAG, "Proxy not attached to service");
899 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
900 }
901 return false;
902 }
903
904 /**
Jack He896e1292018-05-02 19:10:56 -0700905 * Initiates a disconnection of HFP SCO audio.
906 * Tear down voice recognition or virtual voice call if any.
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800907 *
Jack He896e1292018-05-02 19:10:56 -0700908 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
909 * If this function returns true, this intent will be broadcasted with
910 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
911 *
912 * @return false if audio is not connected, or on error, true otherwise
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800913 * @hide
914 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100915 @UnsupportedAppUsage
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800916 public boolean disconnectAudio() {
Jack He16eeac32017-08-17 12:11:18 -0700917 final IBluetoothHeadset service = mService;
918 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800919 try {
Jack He16eeac32017-08-17 12:11:18 -0700920 return service.disconnectAudio();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800921 } catch (RemoteException e) {
922 Log.e(TAG, e.toString());
923 }
924 } else {
925 Log.w(TAG, "Proxy not attached to service");
926 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
927 }
928 return false;
929 }
930
931 /**
Jack He896e1292018-05-02 19:10:56 -0700932 * Initiates a SCO channel connection as a virtual voice call to the current active device
933 * Active handsfree device will be notified of incoming call and connected call.
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700934 *
Jack He896e1292018-05-02 19:10:56 -0700935 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
936 * If this function returns true, this intent will be broadcasted with
937 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
938 *
939 * <p> {@link #EXTRA_STATE} will transition from
940 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
941 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
942 * in case of failure to establish the audio connection.
943 *
944 * @return true if successful, false if one of the following case applies
945 * - SCO audio is not idle (connecting or connected)
946 * - virtual call has already started
947 * - there is no active device
948 * - a Telecom managed call is going on
949 * - binder is dead or Bluetooth is disabled or other error
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700950 * @hide
951 */
Jack He896e1292018-05-02 19:10:56 -0700952 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100953 @UnsupportedAppUsage
Jack He896e1292018-05-02 19:10:56 -0700954 public boolean startScoUsingVirtualVoiceCall() {
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800955 if (DBG) log("startScoUsingVirtualVoiceCall()");
Jack He16eeac32017-08-17 12:11:18 -0700956 final IBluetoothHeadset service = mService;
Jack He896e1292018-05-02 19:10:56 -0700957 if (service != null && isEnabled()) {
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700958 try {
Jack He896e1292018-05-02 19:10:56 -0700959 return service.startScoUsingVirtualVoiceCall();
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700960 } catch (RemoteException e) {
961 Log.e(TAG, e.toString());
962 }
963 } else {
964 Log.w(TAG, "Proxy not attached to service");
965 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
966 }
967 return false;
968 }
969
970 /**
Jack He896e1292018-05-02 19:10:56 -0700971 * Terminates an ongoing SCO connection and the associated virtual call.
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700972 *
Jack He896e1292018-05-02 19:10:56 -0700973 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
974 * If this function returns true, this intent will be broadcasted with
975 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
976 *
977 * @return true if successful, false if one of the following case applies
978 * - virtual voice call is not started or has ended
979 * - binder is dead or Bluetooth is disabled or other error
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700980 * @hide
981 */
Jack He896e1292018-05-02 19:10:56 -0700982 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100983 @UnsupportedAppUsage
Jack He896e1292018-05-02 19:10:56 -0700984 public boolean stopScoUsingVirtualVoiceCall() {
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800985 if (DBG) log("stopScoUsingVirtualVoiceCall()");
Jack He16eeac32017-08-17 12:11:18 -0700986 final IBluetoothHeadset service = mService;
Jack He896e1292018-05-02 19:10:56 -0700987 if (service != null && isEnabled()) {
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700988 try {
Jack He896e1292018-05-02 19:10:56 -0700989 return service.stopScoUsingVirtualVoiceCall();
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700990 } catch (RemoteException e) {
991 Log.e(TAG, e.toString());
992 }
993 } else {
994 Log.w(TAG, "Proxy not attached to service");
995 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
996 }
997 return false;
998 }
999
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001000 /**
1001 * Notify Headset of phone state change.
1002 * This is a backdoor for phone app to call BluetoothHeadset since
1003 * there is currently not a good way to get precise call state change outside
1004 * of phone app.
1005 *
1006 * @hide
1007 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001008 @UnsupportedAppUsage
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001009 public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
Benson Li1d4f64a2018-07-17 18:19:59 +08001010 int type, String name) {
Jack He16eeac32017-08-17 12:11:18 -07001011 final IBluetoothHeadset service = mService;
1012 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001013 try {
Benson Li1d4f64a2018-07-17 18:19:59 +08001014 service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001015 } catch (RemoteException e) {
1016 Log.e(TAG, e.toString());
1017 }
1018 } else {
1019 Log.w(TAG, "Proxy not attached to service");
1020 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1021 }
1022 }
1023
1024 /**
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001025 * Send Headset of CLCC response
1026 *
1027 * @hide
1028 */
1029 public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
Jack Hea355e5e2017-08-22 16:06:54 -07001030 String number, int type) {
Jack He16eeac32017-08-17 12:11:18 -07001031 final IBluetoothHeadset service = mService;
1032 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001033 try {
Jack He16eeac32017-08-17 12:11:18 -07001034 service.clccResponse(index, direction, status, mode, mpty, number, type);
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001035 } catch (RemoteException e) {
1036 Log.e(TAG, e.toString());
1037 }
1038 } else {
1039 Log.w(TAG, "Proxy not attached to service");
1040 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1041 }
1042 }
1043
Edward Jee922d41b2013-08-16 04:07:49 -07001044 /**
1045 * Sends a vendor-specific unsolicited result code to the headset.
1046 *
Jack Hea355e5e2017-08-22 16:06:54 -07001047 * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code
1048 * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the
1049 * string <code>"+ANDROID: 0"</code> will be sent.
Edward Jee922d41b2013-08-16 04:07:49 -07001050 *
Ying Wangb2405782013-08-26 17:48:22 -07001051 * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
Edward Jee922d41b2013-08-16 04:07:49 -07001052 *
1053 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1054 *
1055 * @param device Bluetooth headset.
1056 * @param command A vendor-specific command.
1057 * @param arg The argument that will be attached to the command.
1058 * @return {@code false} if there is no headset connected, or if the command is not an allowed
Jack Hea355e5e2017-08-22 16:06:54 -07001059 * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
Edward Jee922d41b2013-08-16 04:07:49 -07001060 * @throws IllegalArgumentException if {@code command} is {@code null}.
1061 */
1062 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
1063 String arg) {
1064 if (DBG) {
1065 log("sendVendorSpecificResultCode()");
1066 }
1067 if (command == null) {
1068 throw new IllegalArgumentException("command is null");
1069 }
Jack He16eeac32017-08-17 12:11:18 -07001070 final IBluetoothHeadset service = mService;
1071 if (service != null && isEnabled() && isValidDevice(device)) {
Edward Jee922d41b2013-08-16 04:07:49 -07001072 try {
Jack He16eeac32017-08-17 12:11:18 -07001073 return service.sendVendorSpecificResultCode(device, command, arg);
Edward Jee922d41b2013-08-16 04:07:49 -07001074 } catch (RemoteException e) {
1075 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1076 }
1077 }
Jack He16eeac32017-08-17 12:11:18 -07001078 if (service == null) {
Edward Jee922d41b2013-08-16 04:07:49 -07001079 Log.w(TAG, "Proxy not attached to service");
1080 }
1081 return false;
1082 }
1083
Mudumba Ananth177d0782014-04-27 13:11:00 -07001084 /**
Jack He05f4bc42018-01-03 12:13:26 -08001085 * Select a connected device as active.
1086 *
1087 * The active device selection is per profile. An active device's
1088 * purpose is profile-specific. For example, in HFP and HSP profiles,
1089 * it is the device used for phone call audio. If a remote device is not
1090 * connected, it cannot be selected as active.
1091 *
1092 * <p> This API returns false in scenarios like the profile on the
1093 * device is not connected or Bluetooth is not turned on.
1094 * When this API returns true, it is guaranteed that the
1095 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
1096 * with the active device.
1097 *
1098 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
1099 * permission.
1100 *
1101 * @param device Remote Bluetooth Device, could be null if phone call audio should not be
1102 * streamed to a headset
1103 * @return false on immediate error, true otherwise
1104 * @hide
1105 */
1106 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001107 @UnsupportedAppUsage
Jack He05f4bc42018-01-03 12:13:26 -08001108 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
1109 if (DBG) {
1110 Log.d(TAG, "setActiveDevice: " + device);
1111 }
1112 final IBluetoothHeadset service = mService;
1113 if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
1114 try {
1115 return service.setActiveDevice(device);
1116 } catch (RemoteException e) {
1117 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1118 }
1119 }
1120 if (service == null) {
1121 Log.w(TAG, "Proxy not attached to service");
1122 }
1123 return false;
1124 }
1125
1126 /**
1127 * Get the connected device that is active.
1128 *
1129 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1130 * permission.
1131 *
1132 * @return the connected device that is active or null if no device
1133 * is active.
1134 * @hide
1135 */
1136 @RequiresPermission(android.Manifest.permission.BLUETOOTH)
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001137 @UnsupportedAppUsage
Jack He05f4bc42018-01-03 12:13:26 -08001138 public BluetoothDevice getActiveDevice() {
1139 if (VDBG) {
1140 Log.d(TAG, "getActiveDevice");
1141 }
1142 final IBluetoothHeadset service = mService;
1143 if (service != null && isEnabled()) {
1144 try {
1145 return service.getActiveDevice();
1146 } catch (RemoteException e) {
1147 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1148 }
1149 }
1150 if (service == null) {
1151 Log.w(TAG, "Proxy not attached to service");
1152 }
1153 return null;
1154 }
1155
1156 /**
Jack Hebc9976e2018-01-05 17:17:06 -08001157 * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an
1158 * active connection.
Jack Hee86bdca2016-11-17 16:19:43 -08001159 *
Jack Hebc9976e2018-01-05 17:17:06 -08001160 * @return true if in-band ringing is enabled, false if in-band ringing is disabled
1161 * @hide
1162 */
1163 @RequiresPermission(android.Manifest.permission.BLUETOOTH)
1164 public boolean isInbandRingingEnabled() {
1165 if (DBG) {
1166 log("isInbandRingingEnabled()");
1167 }
1168 final IBluetoothHeadset service = mService;
1169 if (service != null && isEnabled()) {
1170 try {
1171 return service.isInbandRingingEnabled();
1172 } catch (RemoteException e) {
1173 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1174 }
1175 }
1176 if (service == null) {
1177 Log.w(TAG, "Proxy not attached to service");
1178 }
1179 return false;
1180 }
1181
1182 /**
1183 * Check if in-band ringing is supported for this platform.
1184 *
1185 * @return true if in-band ringing is supported, false if in-band ringing is not supported
Jack Hee86bdca2016-11-17 16:19:43 -08001186 * @hide
1187 */
1188 public static boolean isInbandRingingSupported(Context context) {
1189 return context.getResources().getBoolean(
1190 com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
1191 }
1192
Jack He2992cd02017-08-22 21:21:23 -07001193 private final IBluetoothProfileServiceConnection mConnection =
1194 new IBluetoothProfileServiceConnection.Stub() {
Benjamin Franze8b98922014-11-12 15:57:54 +00001195 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 public void onServiceConnected(ComponentName className, IBinder service) {
1197 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -06001198 mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
Benjamin Franze8b98922014-11-12 15:57:54 +00001199 mHandler.sendMessage(mHandler.obtainMessage(
1200 MESSAGE_HEADSET_SERVICE_CONNECTED));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 }
Jack Hea355e5e2017-08-22 16:06:54 -07001202
Benjamin Franze8b98922014-11-12 15:57:54 +00001203 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 public void onServiceDisconnected(ComponentName className) {
1205 if (DBG) Log.d(TAG, "Proxy object disconnected");
Ugo Yud0115462019-03-26 21:38:08 +08001206 doUnbind();
Benjamin Franze8b98922014-11-12 15:57:54 +00001207 mHandler.sendMessage(mHandler.obtainMessage(
1208 MESSAGE_HEADSET_SERVICE_DISCONNECTED));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 }
1210 };
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -08001211
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001212 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001213 private boolean isEnabled() {
Jack He16eeac32017-08-17 12:11:18 -07001214 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001215 }
1216
Jaikumar Ganesh6f7a9732011-04-06 11:09:30 -07001217 private boolean isDisabled() {
Jack He16eeac32017-08-17 12:11:18 -07001218 return mAdapter.getState() == BluetoothAdapter.STATE_OFF;
Jaikumar Ganesh6f7a9732011-04-06 11:09:30 -07001219 }
1220
Jack He16eeac32017-08-17 12:11:18 -07001221 private static boolean isValidDevice(BluetoothDevice device) {
1222 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001223 }
1224
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -08001225 private static void log(String msg) {
1226 Log.d(TAG, msg);
1227 }
Benjamin Franze8b98922014-11-12 15:57:54 +00001228
1229 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
1230 @Override
1231 public void handleMessage(Message msg) {
1232 switch (msg.what) {
1233 case MESSAGE_HEADSET_SERVICE_CONNECTED: {
1234 if (mServiceListener != null) {
1235 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
1236 BluetoothHeadset.this);
1237 }
1238 break;
1239 }
1240 case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
1241 if (mServiceListener != null) {
1242 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
1243 }
Benjamin Franze8b98922014-11-12 15:57:54 +00001244 break;
1245 }
1246 }
1247 }
1248 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249}