blob: 8d9d340ee68b4e4b2b573bc4420274a1c5fd2d95 [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;
Jack He05f4bc42018-01-03 12:13:26 -080020import android.annotation.Nullable;
Selim Gurun4029fa62017-10-17 17:01:38 -070021import android.annotation.RequiresPermission;
Nick Pelly005b2282009-09-10 10:21:56 -070022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Selim Gurun4029fa62017-10-17 17:01:38 -070024import android.annotation.SystemApi;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010025import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.ComponentName;
27import android.content.Context;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060028import android.os.Binder;
Benjamin Franze8b98922014-11-12 15:57:54 +000029import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.os.IBinder;
Benjamin Franze8b98922014-11-12 15:57:54 +000031import android.os.Looper;
32import android.os.Message;
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070033import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.util.Log;
35
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070036import java.util.ArrayList;
37import java.util.List;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039/**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 * Public API for controlling the Bluetooth Headset Service. This includes both
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070041 * Bluetooth Headset and Handsfree (v1.5) profiles.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070043 * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 * Service via IPC.
45 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070046 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
47 * the BluetoothHeadset proxy object. Use
48 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070050 * <p> Android only supports one connected Bluetooth Headset at a time.
51 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070053public final class BluetoothHeadset implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 private static final String TAG = "BluetoothHeadset";
fredc0f420372012-04-12 00:02:00 -070055 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070056 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
Nick Pelly005b2282009-09-10 10:21:56 -070058 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070059 * Intent used to broadcast the change in connection state of the Headset
60 * profile.
61 *
62 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080063 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070064 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
65 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
66 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080067 * </ul>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080068 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070069 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
70 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
71 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080072 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
73 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070074 */
75 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
76 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070077 "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070078
79 /**
80 * Intent used to broadcast the change in the Audio Connection state of the
81 * A2DP profile.
82 *
83 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080084 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070085 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
86 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
87 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080088 * </ul>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080089 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070090 * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
91 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080092 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
93 * to receive.
Nick Pelly005b2282009-09-10 10:21:56 -070094 */
95 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
96 public static final String ACTION_AUDIO_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070097 "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
Nick Pelly005b2282009-09-10 10:21:56 -070098
Jack He05f4bc42018-01-03 12:13:26 -080099 /**
100 * Intent used to broadcast the selection of a connected device as active.
101 *
102 * <p>This intent will have one extra:
103 * <ul>
104 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
105 * be null if no device is active. </li>
106 * </ul>
107 *
108 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
109 * receive.
110 *
111 * @hide
112 */
113 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100114 @UnsupportedAppUsage
Jack He05f4bc42018-01-03 12:13:26 -0800115 public static final String ACTION_ACTIVE_DEVICE_CHANGED =
116 "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
Jaikumar Ganeshc24dbdb2010-04-02 14:44:43 -0700117
Nick Pelly005b2282009-09-10 10:21:56 -0700118 /**
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700119 * Intent used to broadcast that the headset has posted a
120 * vendor-specific event.
121 *
122 * <p>This intent will have 4 extras and 1 category.
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800123 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700124 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
125 * </li>
126 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
127 * specific command </li>
128 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
129 * command type which can be one of {@link #AT_CMD_TYPE_READ},
130 * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
131 * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
132 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
133 * arguments. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800134 * </ul>
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700135 *
Jack Hea355e5e2017-08-22 16:06:54 -0700136 * <p> The category is the Company ID of the vendor defining the
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700137 * vendor-specific command. {@link BluetoothAssignedNumbers}
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700138 *
139 * For example, for Plantronics specific events
140 * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
141 *
142 * <p> For example, an AT+XEVENT=foo,3 will get translated into
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800143 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700144 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
145 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
146 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800147 * </ul>
148 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
149 * to receive.
Herb Jellineka4733942010-08-10 13:17:43 -0700150 */
151 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
152 public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
153 "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
154
155 /**
156 * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
157 * intents that contains the name of the vendor-specific command.
158 */
159 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
160 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
161
162 /**
163 * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700164 * intents that contains the AT command type of the vendor-specific command.
Herb Jellineka4733942010-08-10 13:17:43 -0700165 */
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700166 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
167 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
168
169 /**
170 * AT command type READ used with
171 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
172 * For example, AT+VGM?. There are no arguments for this command type.
173 */
174 public static final int AT_CMD_TYPE_READ = 0;
175
176 /**
177 * AT command type TEST used with
178 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
179 * For example, AT+VGM=?. There are no arguments for this command type.
180 */
181 public static final int AT_CMD_TYPE_TEST = 1;
182
183 /**
184 * AT command type SET used with
185 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
186 * For example, AT+VGM=<args>.
187 */
188 public static final int AT_CMD_TYPE_SET = 2;
189
190 /**
191 * AT command type BASIC used with
192 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
193 * For example, ATD. Single character commands and everything following the
194 * character are arguments.
195 */
196 public static final int AT_CMD_TYPE_BASIC = 3;
197
198 /**
199 * AT command type ACTION used with
200 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
201 * For example, AT+CHUP. There are no arguments for action commands.
202 */
203 public static final int AT_CMD_TYPE_ACTION = 4;
Herb Jellineka4733942010-08-10 13:17:43 -0700204
205 /**
206 * A Parcelable String array extra field in
207 * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
208 * the arguments to the vendor-specific command.
209 */
210 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
211 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
212
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700213 /**
214 * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
215 * for the companyId
216 */
Jack Hea355e5e2017-08-22 16:06:54 -0700217 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700218 "android.bluetooth.headset.intent.category.companyid";
219
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700220 /**
Edward Jee922d41b2013-08-16 04:07:49 -0700221 * A vendor-specific command for unsolicited result code.
222 */
223 public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
224
225 /**
Jack He0fcbba22017-06-20 17:07:40 -0700226 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700227 *
Jack He0fcbba22017-06-20 17:07:40 -0700228 * @hide
229 */
Jack He679d0bc2017-06-20 17:09:47 -0700230 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
231
232 /**
233 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700234 *
Jack He679d0bc2017-06-20 17:09:47 -0700235 * @hide
236 */
237 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
238
239 /**
240 * Battery level indicator associated with
241 * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
Jack Hea355e5e2017-08-22 16:06:54 -0700242 *
Jack He679d0bc2017-06-20 17:09:47 -0700243 * @hide
244 */
245 public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
246
247 /**
248 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700249 *
Jack He679d0bc2017-06-20 17:09:47 -0700250 * @hide
251 */
Jack He0fcbba22017-06-20 17:07:40 -0700252 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
253
254 /**
255 * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
Jack Hea355e5e2017-08-22 16:06:54 -0700256 *
Jack He0fcbba22017-06-20 17:07:40 -0700257 * @hide
258 */
259 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
260
261 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800262 * Headset state when SCO audio is not connected.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700263 * This state can be one of
264 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
265 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
266 */
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800267 public static final int STATE_AUDIO_DISCONNECTED = 10;
Herb Jellineka4733942010-08-10 13:17:43 -0700268
269 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800270 * Headset state when SCO audio is connecting.
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700271 * This state can be one of
272 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
273 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700274 */
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800275 public static final int STATE_AUDIO_CONNECTING = 11;
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700276
277 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800278 * Headset state when SCO audio is connected.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700279 * This state can be one of
280 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
281 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Nick Pelly005b2282009-09-10 10:21:56 -0700282 */
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800283
284 /**
285 * Intent used to broadcast the headset's indicator status
286 *
287 * <p>This intent will have 3 extras:
288 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700289 * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
290 * is supported by the headset ( as indicated by AT+BIND command in the SLC
291 * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
292 * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
293 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800294 * </ul>
Jack He73795442017-06-22 12:56:54 -0700295 * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
Jack Hea355e5e2017-08-22 16:06:54 -0700296 * are given an assigned number. Below shows the assigned number of Indicator added so far
Jack He73795442017-06-22 12:56:54 -0700297 * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
298 * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
299 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
Jack Hea355e5e2017-08-22 16:06:54 -0700300 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800301 * @hide
302 */
303 public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
304 "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
305
306 /**
Jack He73795442017-06-22 12:56:54 -0700307 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
308 * intents that contains the assigned number of the headset indicator as defined by
309 * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
Jack Hea355e5e2017-08-22 16:06:54 -0700310 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800311 * @hide
312 */
313 public static final String EXTRA_HF_INDICATORS_IND_ID =
314 "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
315
316 /**
Jack He73795442017-06-22 12:56:54 -0700317 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800318 * intents that contains the value of the Headset indicator that is being sent.
Jack Hea355e5e2017-08-22 16:06:54 -0700319 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800320 * @hide
321 */
322 public static final String EXTRA_HF_INDICATORS_IND_VALUE =
323 "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
324
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800325 public static final int STATE_AUDIO_CONNECTED = 12;
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) {
Jack Hea355e5e2017-08-22 16:06:54 -0700340 if (VDBG) Log.d(TAG, "Unbinding service...");
Benjamin Franze8b98922014-11-12 15:57:54 +0000341 doUnbind();
fredc0f420372012-04-12 00:02:00 -0700342 } else {
343 synchronized (mConnection) {
344 try {
345 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700346 if (VDBG) Log.d(TAG, "Binding service...");
Dianne Hackborn221ea892013-08-04 16:50:16 -0700347 doBind();
fredc0f420372012-04-12 00:02:00 -0700348 }
349 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700350 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700351 }
352 }
353 }
354 }
Jack Hea355e5e2017-08-22 16:06:54 -0700355 };
fredc0f420372012-04-12 00:02:00 -0700356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 /**
358 * Create a BluetoothHeadset proxy object.
359 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700360 /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 mContext = context;
362 mServiceListener = l;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700363 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700364
365 IBluetoothManager mgr = mAdapter.getBluetoothManager();
366 if (mgr != null) {
367 try {
368 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
369 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700370 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700371 }
372 }
373
Dianne Hackborn221ea892013-08-04 16:50:16 -0700374 doBind();
375 }
376
377 boolean doBind() {
Benjamin Franze8b98922014-11-12 15:57:54 +0000378 try {
379 return mAdapter.getBluetoothManager().bindBluetoothProfileService(
380 BluetoothProfile.HEADSET, mConnection);
381 } catch (RemoteException e) {
382 Log.e(TAG, "Unable to bind HeadsetService", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 }
Benjamin Franze8b98922014-11-12 15:57:54 +0000384 return false;
385 }
386
387 void doUnbind() {
388 synchronized (mConnection) {
389 if (mService != null) {
390 try {
391 mAdapter.getBluetoothManager().unbindBluetoothProfileService(
392 BluetoothProfile.HEADSET, mConnection);
393 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700394 Log.e(TAG, "Unable to unbind HeadsetService", e);
Benjamin Franze8b98922014-11-12 15:57:54 +0000395 }
396 }
397 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 }
399
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 /**
401 * Close the connection to the backing service.
402 * Other public functions of BluetoothHeadset will return default error
403 * results once close() has been called. Multiple invocations of close()
404 * are ok.
405 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100406 @UnsupportedAppUsage
Matthew Xie13450df2012-03-22 17:18:37 -0700407 /*package*/ void close() {
Matthew Xie563e4142012-10-09 22:10:37 -0700408 if (VDBG) log("close()");
fredc0f420372012-04-12 00:02:00 -0700409
410 IBluetoothManager mgr = mAdapter.getBluetoothManager();
411 if (mgr != null) {
412 try {
413 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
414 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700415 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700416 }
417 }
Benjamin Franzc88b6bd2014-12-16 15:33:03 +0000418 mServiceListener = null;
Benjamin Franze8b98922014-11-12 15:57:54 +0000419 doUnbind();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 }
421
422 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700423 * Initiate connection to a profile of the remote bluetooth device.
424 *
425 * <p> Currently, the system supports only 1 connection to the
426 * headset/handsfree profile. The API will automatically disconnect connected
427 * devices before connecting.
428 *
429 * <p> This API returns false in scenarios like the profile on the
430 * device is already connected or Bluetooth is not turned on.
431 * When this API returns true, it is guaranteed that
432 * connection state intent for the profile will be broadcasted with
433 * the state. Users can get the connection state of the profile
434 * from this intent.
435 *
436 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
437 * permission.
438 *
439 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700440 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700441 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 */
Selim Gurun4029fa62017-10-17 17:01:38 -0700443 @SystemApi
444 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700445 public boolean connect(BluetoothDevice device) {
446 if (DBG) log("connect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700447 final IBluetoothHeadset service = mService;
448 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 try {
Jack He16eeac32017-08-17 12:11:18 -0700450 return service.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700451 } catch (RemoteException e) {
452 Log.e(TAG, Log.getStackTraceString(new Throwable()));
453 return false;
454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 }
Jack He16eeac32017-08-17 12:11:18 -0700456 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 return false;
458 }
459
460 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700461 * Initiate disconnection from a profile
462 *
463 * <p> This API will return false in scenarios like the profile on the
464 * Bluetooth device is not in connected state etc. When this API returns,
465 * true, it is guaranteed that the connection state change
466 * intent will be broadcasted with the state. Users can get the
467 * disconnection state of the profile from this intent.
468 *
469 * <p> If the disconnection is initiated by a remote device, the state
470 * will transition from {@link #STATE_CONNECTED} to
471 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
472 * host (local) device the state will transition from
473 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
474 * state {@link #STATE_DISCONNECTED}. The transition to
475 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
476 * two scenarios.
477 *
478 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
479 * permission.
480 *
481 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700482 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700483 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 */
Selim Gurun4029fa62017-10-17 17:01:38 -0700485 @SystemApi
486 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700487 public boolean disconnect(BluetoothDevice device) {
488 if (DBG) log("disconnect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700489 final IBluetoothHeadset service = mService;
490 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 try {
Jack He16eeac32017-08-17 12:11:18 -0700492 return service.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700493 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700494 Log.e(TAG, Log.getStackTraceString(new Throwable()));
495 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700496 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 }
Jack He16eeac32017-08-17 12:11:18 -0700498 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 return false;
500 }
501
502 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700503 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 */
Jack He2992cd02017-08-22 21:21:23 -0700505 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700506 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700507 if (VDBG) log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700508 final IBluetoothHeadset service = mService;
509 if (service != null && isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 try {
Jack He16eeac32017-08-17 12:11:18 -0700511 return service.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700512 } catch (RemoteException e) {
513 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700514 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700515 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
Jack He16eeac32017-08-17 12:11:18 -0700517 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700518 return new ArrayList<BluetoothDevice>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 }
520
521 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700522 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 */
Jack He2992cd02017-08-22 21:21:23 -0700524 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700525 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700526 if (VDBG) log("getDevicesMatchingStates()");
Jack He16eeac32017-08-17 12:11:18 -0700527 final IBluetoothHeadset service = mService;
528 if (service != null && isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 try {
Jack He16eeac32017-08-17 12:11:18 -0700530 return service.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700531 } catch (RemoteException e) {
532 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700533 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700534 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 }
Jack He16eeac32017-08-17 12:11:18 -0700536 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700537 return new ArrayList<BluetoothDevice>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 }
539
540 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700541 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 */
Jack He2992cd02017-08-22 21:21:23 -0700543 @Override
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700544 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700545 if (VDBG) log("getConnectionState(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700546 final IBluetoothHeadset service = mService;
547 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 try {
Jack He16eeac32017-08-17 12:11:18 -0700549 return service.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700550 } catch (RemoteException e) {
551 Log.e(TAG, Log.getStackTraceString(new Throwable()));
552 return BluetoothProfile.STATE_DISCONNECTED;
553 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 }
Jack He16eeac32017-08-17 12:11:18 -0700555 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700556 return BluetoothProfile.STATE_DISCONNECTED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 }
558
559 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700560 * Set priority of the profile
561 *
562 * <p> The device should already be paired.
Selim Gurun17a26c72018-01-09 14:35:19 -0800563 * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
564 * {@link BluetoothProfile#PRIORITY_OFF},
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700565 *
566 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
567 * permission.
568 *
569 * @param device Paired bluetooth device
570 * @param priority
571 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700572 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 */
Selim Gurun4029fa62017-10-17 17:01:38 -0700574 @SystemApi
575 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Nick Pellybd022f42009-08-14 18:33:38 -0700576 public boolean setPriority(BluetoothDevice device, int priority) {
577 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Jack He16eeac32017-08-17 12:11:18 -0700578 final IBluetoothHeadset service = mService;
579 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700580 if (priority != BluetoothProfile.PRIORITY_OFF
581 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700582 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 try {
Jack He16eeac32017-08-17 12:11:18 -0700585 return service.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700586 } catch (RemoteException e) {
587 Log.e(TAG, Log.getStackTraceString(new Throwable()));
588 return false;
589 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 }
Jack He16eeac32017-08-17 12:11:18 -0700591 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 return false;
593 }
594
595 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700596 * Get the priority of the profile.
597 *
598 * <p> The priority can be any of:
599 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
600 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
601 *
602 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
603 *
604 * @param device Bluetooth device
605 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700606 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100608 @UnsupportedAppUsage
Nick Pellybd022f42009-08-14 18:33:38 -0700609 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700610 if (VDBG) log("getPriority(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700611 final IBluetoothHeadset service = mService;
612 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 try {
Jack He16eeac32017-08-17 12:11:18 -0700614 return service.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700615 } catch (RemoteException e) {
616 Log.e(TAG, Log.getStackTraceString(new Throwable()));
617 return PRIORITY_OFF;
618 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 }
Jack He16eeac32017-08-17 12:11:18 -0700620 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700621 return PRIORITY_OFF;
622 }
623
624 /**
625 * Start Bluetooth voice recognition. This methods sends the voice
626 * recognition AT command to the headset and establishes the
627 * audio connection.
628 *
629 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800630 * If this function returns true, this intent will be broadcasted with
631 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700632 *
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800633 * <p> {@link #EXTRA_STATE} will transition from
634 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
635 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
636 * in case of failure to establish the audio connection.
637 *
638 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700639 *
640 * @param device Bluetooth headset
Jack He896e1292018-05-02 19:10:56 -0700641 * @return false if there is no headset connected, or the connected headset doesn't support
642 * voice recognition, or voice recognition is already started, or audio channel is occupied,
643 * or on error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700644 */
645 public boolean startVoiceRecognition(BluetoothDevice device) {
646 if (DBG) log("startVoiceRecognition()");
Jack He16eeac32017-08-17 12:11:18 -0700647 final IBluetoothHeadset service = mService;
648 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700649 try {
Jack He16eeac32017-08-17 12:11:18 -0700650 return service.startVoiceRecognition(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700651 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700652 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700653 }
654 }
Jack He16eeac32017-08-17 12:11:18 -0700655 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700656 return false;
657 }
658
659 /**
660 * Stop Bluetooth Voice Recognition mode, and shut down the
661 * Bluetooth audio path.
662 *
Jack He896e1292018-05-02 19:10:56 -0700663 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
664 * If this function returns true, this intent will be broadcasted with
665 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
666 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800667 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700668 *
669 * @param device Bluetooth headset
Jack He896e1292018-05-02 19:10:56 -0700670 * @return false if there is no headset connected, or voice recognition has not started,
671 * or voice recognition has ended on this headset, or on error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700672 */
673 public boolean stopVoiceRecognition(BluetoothDevice device) {
674 if (DBG) log("stopVoiceRecognition()");
Jack He16eeac32017-08-17 12:11:18 -0700675 final IBluetoothHeadset service = mService;
676 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700677 try {
Jack He16eeac32017-08-17 12:11:18 -0700678 return service.stopVoiceRecognition(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700679 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700680 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700681 }
682 }
Jack He16eeac32017-08-17 12:11:18 -0700683 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700684 return false;
685 }
686
687 /**
688 * Check if Bluetooth SCO audio is connected.
689 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800690 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700691 *
692 * @param device Bluetooth headset
Jack Hea355e5e2017-08-22 16:06:54 -0700693 * @return true if SCO is connected, false otherwise or on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700694 */
695 public boolean isAudioConnected(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700696 if (VDBG) log("isAudioConnected()");
Jack He16eeac32017-08-17 12:11:18 -0700697 final IBluetoothHeadset service = mService;
698 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700699 try {
Jack He16eeac32017-08-17 12:11:18 -0700700 return service.isAudioConnected(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700701 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700702 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700703 }
704 }
Jack He16eeac32017-08-17 12:11:18 -0700705 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700706 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 }
708
709 /**
Eric Laurentd726b352010-03-17 14:59:27 -0700710 * Indicates if current platform supports voice dialing over bluetooth SCO.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700711 *
Eric Laurentd726b352010-03-17 14:59:27 -0700712 * @return true if voice dialing over bluetooth is supported, false otherwise.
713 * @hide
714 */
715 public static boolean isBluetoothVoiceDialingEnabled(Context context) {
716 return context.getResources().getBoolean(
717 com.android.internal.R.bool.config_bluetooth_sco_off_call);
718 }
719
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700720 /**
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700721 * Get the current audio state of the Headset.
722 * Note: This is an internal function and shouldn't be exposed
723 *
724 * @hide
725 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100726 @UnsupportedAppUsage
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700727 public int getAudioState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700728 if (VDBG) log("getAudioState");
Jack He16eeac32017-08-17 12:11:18 -0700729 final IBluetoothHeadset service = mService;
730 if (service != null && !isDisabled()) {
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700731 try {
Jack He16eeac32017-08-17 12:11:18 -0700732 return service.getAudioState(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700733 } catch (RemoteException e) {
734 Log.e(TAG, e.toString());
735 }
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700736 } else {
737 Log.w(TAG, "Proxy not attached to service");
738 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
739 }
740 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
741 }
742
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700743 /**
Bryce Lee0db53d92015-11-16 08:55:52 -0800744 * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
745 * audio to the HF unless explicitly told to.
746 * This method should be used in cases where the SCO channel is shared between multiple profiles
747 * and must be delegated by a source knowledgeable
748 * Note: This is an internal function and shouldn't be exposed
749 *
750 * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
Bryce Lee0db53d92015-11-16 08:55:52 -0800751 * @hide
752 */
753 public void setAudioRouteAllowed(boolean allowed) {
754 if (VDBG) log("setAudioRouteAllowed");
Jack He16eeac32017-08-17 12:11:18 -0700755 final IBluetoothHeadset service = mService;
756 if (service != null && isEnabled()) {
Bryce Lee0db53d92015-11-16 08:55:52 -0800757 try {
Jack He16eeac32017-08-17 12:11:18 -0700758 service.setAudioRouteAllowed(allowed);
Jack Hea355e5e2017-08-22 16:06:54 -0700759 } catch (RemoteException e) {
760 Log.e(TAG, e.toString());
761 }
Bryce Lee0db53d92015-11-16 08:55:52 -0800762 } else {
763 Log.w(TAG, "Proxy not attached to service");
764 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
765 }
766 }
767
768 /**
769 * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
770 * Note: This is an internal function and shouldn't be exposed
771 *
772 * @hide
773 */
774 public boolean getAudioRouteAllowed() {
775 if (VDBG) log("getAudioRouteAllowed");
Jack He16eeac32017-08-17 12:11:18 -0700776 final IBluetoothHeadset service = mService;
777 if (service != null && isEnabled()) {
Bryce Lee0db53d92015-11-16 08:55:52 -0800778 try {
Jack He16eeac32017-08-17 12:11:18 -0700779 return service.getAudioRouteAllowed();
Jack Hea355e5e2017-08-22 16:06:54 -0700780 } catch (RemoteException e) {
781 Log.e(TAG, e.toString());
782 }
Bryce Lee0db53d92015-11-16 08:55:52 -0800783 } else {
784 Log.w(TAG, "Proxy not attached to service");
785 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
786 }
787 return false;
788 }
789
790 /**
Jack He1dd1e032017-05-09 17:16:01 -0700791 * Force SCO audio to be opened regardless any other restrictions
792 *
Jack Hea355e5e2017-08-22 16:06:54 -0700793 * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio
794 * False to use SCO audio in normal manner
Jack He1dd1e032017-05-09 17:16:01 -0700795 * @hide
796 */
797 public void setForceScoAudio(boolean forced) {
798 if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
Jack He16eeac32017-08-17 12:11:18 -0700799 final IBluetoothHeadset service = mService;
800 if (service != null && isEnabled()) {
Jack He1dd1e032017-05-09 17:16:01 -0700801 try {
Jack He16eeac32017-08-17 12:11:18 -0700802 service.setForceScoAudio(forced);
Jack He1dd1e032017-05-09 17:16:01 -0700803 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700804 Log.e(TAG, e.toString());
Jack He1dd1e032017-05-09 17:16:01 -0700805 }
806 } else {
807 Log.w(TAG, "Proxy not attached to service");
808 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
809 }
810 }
811
812 /**
Jack He896e1292018-05-02 19:10:56 -0700813 * Check if at least one headset's SCO audio is connected or connecting
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800814 *
815 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
816 *
Jack He896e1292018-05-02 19:10:56 -0700817 * @return true if at least one device's SCO audio is connected or connecting, false otherwise
818 * or on error
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800819 * @hide
820 */
821 public boolean isAudioOn() {
Matthew Xie563e4142012-10-09 22:10:37 -0700822 if (VDBG) log("isAudioOn()");
Jack He16eeac32017-08-17 12:11:18 -0700823 final IBluetoothHeadset service = mService;
824 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800825 try {
Jack He16eeac32017-08-17 12:11:18 -0700826 return service.isAudioOn();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800827 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700828 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800829 }
830 }
Jack He16eeac32017-08-17 12:11:18 -0700831 if (service == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800832 return false;
833
834 }
835
836 /**
Jack He896e1292018-05-02 19:10:56 -0700837 * Initiates a connection of headset audio to the current active device
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800838 *
Jack He896e1292018-05-02 19:10:56 -0700839 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
840 * If this function returns true, this intent will be broadcasted with
841 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
842 *
843 * <p> {@link #EXTRA_STATE} will transition from
844 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
845 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
846 * in case of failure to establish the audio connection.
847 *
848 * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true
849 * before calling this method
850 *
851 * @return false if there was some error such as there is no active headset
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800852 * @hide
853 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100854 @UnsupportedAppUsage
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800855 public boolean connectAudio() {
Jack He16eeac32017-08-17 12:11:18 -0700856 final IBluetoothHeadset service = mService;
857 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800858 try {
Jack He16eeac32017-08-17 12:11:18 -0700859 return service.connectAudio();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800860 } catch (RemoteException e) {
861 Log.e(TAG, e.toString());
862 }
863 } else {
864 Log.w(TAG, "Proxy not attached to service");
865 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
866 }
867 return false;
868 }
869
870 /**
Jack He896e1292018-05-02 19:10:56 -0700871 * Initiates a disconnection of HFP SCO audio.
872 * Tear down voice recognition or virtual voice call if any.
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800873 *
Jack He896e1292018-05-02 19:10:56 -0700874 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
875 * If this function returns true, this intent will be broadcasted with
876 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
877 *
878 * @return false if audio is not connected, or on error, true otherwise
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800879 * @hide
880 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100881 @UnsupportedAppUsage
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800882 public boolean disconnectAudio() {
Jack He16eeac32017-08-17 12:11:18 -0700883 final IBluetoothHeadset service = mService;
884 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800885 try {
Jack He16eeac32017-08-17 12:11:18 -0700886 return service.disconnectAudio();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800887 } catch (RemoteException e) {
888 Log.e(TAG, e.toString());
889 }
890 } else {
891 Log.w(TAG, "Proxy not attached to service");
892 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
893 }
894 return false;
895 }
896
897 /**
Jack He896e1292018-05-02 19:10:56 -0700898 * Initiates a SCO channel connection as a virtual voice call to the current active device
899 * Active handsfree device will be notified of incoming call and connected call.
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700900 *
Jack He896e1292018-05-02 19:10:56 -0700901 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
902 * If this function returns true, this intent will be broadcasted with
903 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
904 *
905 * <p> {@link #EXTRA_STATE} will transition from
906 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
907 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
908 * in case of failure to establish the audio connection.
909 *
910 * @return true if successful, false if one of the following case applies
911 * - SCO audio is not idle (connecting or connected)
912 * - virtual call has already started
913 * - there is no active device
914 * - a Telecom managed call is going on
915 * - binder is dead or Bluetooth is disabled or other error
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700916 * @hide
917 */
Jack He896e1292018-05-02 19:10:56 -0700918 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100919 @UnsupportedAppUsage
Jack He896e1292018-05-02 19:10:56 -0700920 public boolean startScoUsingVirtualVoiceCall() {
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800921 if (DBG) log("startScoUsingVirtualVoiceCall()");
Jack He16eeac32017-08-17 12:11:18 -0700922 final IBluetoothHeadset service = mService;
Jack He896e1292018-05-02 19:10:56 -0700923 if (service != null && isEnabled()) {
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700924 try {
Jack He896e1292018-05-02 19:10:56 -0700925 return service.startScoUsingVirtualVoiceCall();
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700926 } catch (RemoteException e) {
927 Log.e(TAG, e.toString());
928 }
929 } else {
930 Log.w(TAG, "Proxy not attached to service");
931 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
932 }
933 return false;
934 }
935
936 /**
Jack He896e1292018-05-02 19:10:56 -0700937 * Terminates an ongoing SCO connection and the associated virtual call.
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700938 *
Jack He896e1292018-05-02 19:10:56 -0700939 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
940 * If this function returns true, this intent will be broadcasted with
941 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
942 *
943 * @return true if successful, false if one of the following case applies
944 * - virtual voice call is not started or has ended
945 * - binder is dead or Bluetooth is disabled or other error
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700946 * @hide
947 */
Jack He896e1292018-05-02 19:10:56 -0700948 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100949 @UnsupportedAppUsage
Jack He896e1292018-05-02 19:10:56 -0700950 public boolean stopScoUsingVirtualVoiceCall() {
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800951 if (DBG) log("stopScoUsingVirtualVoiceCall()");
Jack He16eeac32017-08-17 12:11:18 -0700952 final IBluetoothHeadset service = mService;
Jack He896e1292018-05-02 19:10:56 -0700953 if (service != null && isEnabled()) {
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700954 try {
Jack He896e1292018-05-02 19:10:56 -0700955 return service.stopScoUsingVirtualVoiceCall();
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700956 } catch (RemoteException e) {
957 Log.e(TAG, e.toString());
958 }
959 } else {
960 Log.w(TAG, "Proxy not attached to service");
961 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
962 }
963 return false;
964 }
965
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800966 /**
967 * Notify Headset of phone state change.
968 * This is a backdoor for phone app to call BluetoothHeadset since
969 * there is currently not a good way to get precise call state change outside
970 * of phone app.
971 *
972 * @hide
973 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100974 @UnsupportedAppUsage
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800975 public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
Benson Li1d4f64a2018-07-17 18:19:59 +0800976 int type, String name) {
Jack He16eeac32017-08-17 12:11:18 -0700977 final IBluetoothHeadset service = mService;
978 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800979 try {
Benson Li1d4f64a2018-07-17 18:19:59 +0800980 service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800981 } catch (RemoteException e) {
982 Log.e(TAG, e.toString());
983 }
984 } else {
985 Log.w(TAG, "Proxy not attached to service");
986 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
987 }
988 }
989
990 /**
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800991 * Send Headset of CLCC response
992 *
993 * @hide
994 */
995 public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
Jack Hea355e5e2017-08-22 16:06:54 -0700996 String number, int type) {
Jack He16eeac32017-08-17 12:11:18 -0700997 final IBluetoothHeadset service = mService;
998 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800999 try {
Jack He16eeac32017-08-17 12:11:18 -07001000 service.clccResponse(index, direction, status, mode, mpty, number, type);
Matthew Xie3e8c82e2012-02-16 16:57:18 -08001001 } catch (RemoteException e) {
1002 Log.e(TAG, e.toString());
1003 }
1004 } else {
1005 Log.w(TAG, "Proxy not attached to service");
1006 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1007 }
1008 }
1009
Edward Jee922d41b2013-08-16 04:07:49 -07001010 /**
1011 * Sends a vendor-specific unsolicited result code to the headset.
1012 *
Jack Hea355e5e2017-08-22 16:06:54 -07001013 * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code
1014 * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the
1015 * string <code>"+ANDROID: 0"</code> will be sent.
Edward Jee922d41b2013-08-16 04:07:49 -07001016 *
Ying Wangb2405782013-08-26 17:48:22 -07001017 * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
Edward Jee922d41b2013-08-16 04:07:49 -07001018 *
1019 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1020 *
1021 * @param device Bluetooth headset.
1022 * @param command A vendor-specific command.
1023 * @param arg The argument that will be attached to the command.
1024 * @return {@code false} if there is no headset connected, or if the command is not an allowed
Jack Hea355e5e2017-08-22 16:06:54 -07001025 * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
Edward Jee922d41b2013-08-16 04:07:49 -07001026 * @throws IllegalArgumentException if {@code command} is {@code null}.
1027 */
1028 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
1029 String arg) {
1030 if (DBG) {
1031 log("sendVendorSpecificResultCode()");
1032 }
1033 if (command == null) {
1034 throw new IllegalArgumentException("command is null");
1035 }
Jack He16eeac32017-08-17 12:11:18 -07001036 final IBluetoothHeadset service = mService;
1037 if (service != null && isEnabled() && isValidDevice(device)) {
Edward Jee922d41b2013-08-16 04:07:49 -07001038 try {
Jack He16eeac32017-08-17 12:11:18 -07001039 return service.sendVendorSpecificResultCode(device, command, arg);
Edward Jee922d41b2013-08-16 04:07:49 -07001040 } catch (RemoteException e) {
1041 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1042 }
1043 }
Jack He16eeac32017-08-17 12:11:18 -07001044 if (service == null) {
Edward Jee922d41b2013-08-16 04:07:49 -07001045 Log.w(TAG, "Proxy not attached to service");
1046 }
1047 return false;
1048 }
1049
Mudumba Ananth177d0782014-04-27 13:11:00 -07001050 /**
Jack He05f4bc42018-01-03 12:13:26 -08001051 * Select a connected device as active.
1052 *
1053 * The active device selection is per profile. An active device's
1054 * purpose is profile-specific. For example, in HFP and HSP profiles,
1055 * it is the device used for phone call audio. If a remote device is not
1056 * connected, it cannot be selected as active.
1057 *
1058 * <p> This API returns false in scenarios like the profile on the
1059 * device is not connected or Bluetooth is not turned on.
1060 * When this API returns true, it is guaranteed that the
1061 * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
1062 * with the active device.
1063 *
1064 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
1065 * permission.
1066 *
1067 * @param device Remote Bluetooth Device, could be null if phone call audio should not be
1068 * streamed to a headset
1069 * @return false on immediate error, true otherwise
1070 * @hide
1071 */
1072 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001073 @UnsupportedAppUsage
Jack He05f4bc42018-01-03 12:13:26 -08001074 public boolean setActiveDevice(@Nullable BluetoothDevice device) {
1075 if (DBG) {
1076 Log.d(TAG, "setActiveDevice: " + device);
1077 }
1078 final IBluetoothHeadset service = mService;
1079 if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
1080 try {
1081 return service.setActiveDevice(device);
1082 } catch (RemoteException e) {
1083 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1084 }
1085 }
1086 if (service == null) {
1087 Log.w(TAG, "Proxy not attached to service");
1088 }
1089 return false;
1090 }
1091
1092 /**
1093 * Get the connected device that is active.
1094 *
1095 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1096 * permission.
1097 *
1098 * @return the connected device that is active or null if no device
1099 * is active.
1100 * @hide
1101 */
1102 @RequiresPermission(android.Manifest.permission.BLUETOOTH)
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001103 @UnsupportedAppUsage
Jack He05f4bc42018-01-03 12:13:26 -08001104 public BluetoothDevice getActiveDevice() {
1105 if (VDBG) {
1106 Log.d(TAG, "getActiveDevice");
1107 }
1108 final IBluetoothHeadset service = mService;
1109 if (service != null && isEnabled()) {
1110 try {
1111 return service.getActiveDevice();
1112 } catch (RemoteException e) {
1113 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1114 }
1115 }
1116 if (service == null) {
1117 Log.w(TAG, "Proxy not attached to service");
1118 }
1119 return null;
1120 }
1121
1122 /**
Jack Hebc9976e2018-01-05 17:17:06 -08001123 * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an
1124 * active connection.
Jack Hee86bdca2016-11-17 16:19:43 -08001125 *
Jack Hebc9976e2018-01-05 17:17:06 -08001126 * @return true if in-band ringing is enabled, false if in-band ringing is disabled
1127 * @hide
1128 */
1129 @RequiresPermission(android.Manifest.permission.BLUETOOTH)
1130 public boolean isInbandRingingEnabled() {
1131 if (DBG) {
1132 log("isInbandRingingEnabled()");
1133 }
1134 final IBluetoothHeadset service = mService;
1135 if (service != null && isEnabled()) {
1136 try {
1137 return service.isInbandRingingEnabled();
1138 } catch (RemoteException e) {
1139 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1140 }
1141 }
1142 if (service == null) {
1143 Log.w(TAG, "Proxy not attached to service");
1144 }
1145 return false;
1146 }
1147
1148 /**
1149 * Check if in-band ringing is supported for this platform.
1150 *
1151 * @return true if in-band ringing is supported, false if in-band ringing is not supported
Jack Hee86bdca2016-11-17 16:19:43 -08001152 * @hide
1153 */
1154 public static boolean isInbandRingingSupported(Context context) {
1155 return context.getResources().getBoolean(
1156 com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
1157 }
1158
Jack He2992cd02017-08-22 21:21:23 -07001159 private final IBluetoothProfileServiceConnection mConnection =
1160 new IBluetoothProfileServiceConnection.Stub() {
Benjamin Franze8b98922014-11-12 15:57:54 +00001161 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 public void onServiceConnected(ComponentName className, IBinder service) {
1163 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -06001164 mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
Benjamin Franze8b98922014-11-12 15:57:54 +00001165 mHandler.sendMessage(mHandler.obtainMessage(
1166 MESSAGE_HEADSET_SERVICE_CONNECTED));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 }
Jack Hea355e5e2017-08-22 16:06:54 -07001168
Benjamin Franze8b98922014-11-12 15:57:54 +00001169 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 public void onServiceDisconnected(ComponentName className) {
1171 if (DBG) Log.d(TAG, "Proxy object disconnected");
1172 mService = null;
Benjamin Franze8b98922014-11-12 15:57:54 +00001173 mHandler.sendMessage(mHandler.obtainMessage(
1174 MESSAGE_HEADSET_SERVICE_DISCONNECTED));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 }
1176 };
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -08001177
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001178 @UnsupportedAppUsage
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001179 private boolean isEnabled() {
Jack He16eeac32017-08-17 12:11:18 -07001180 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001181 }
1182
Jaikumar Ganesh6f7a9732011-04-06 11:09:30 -07001183 private boolean isDisabled() {
Jack He16eeac32017-08-17 12:11:18 -07001184 return mAdapter.getState() == BluetoothAdapter.STATE_OFF;
Jaikumar Ganesh6f7a9732011-04-06 11:09:30 -07001185 }
1186
Jack He16eeac32017-08-17 12:11:18 -07001187 private static boolean isValidDevice(BluetoothDevice device) {
1188 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001189 }
1190
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -08001191 private static void log(String msg) {
1192 Log.d(TAG, msg);
1193 }
Benjamin Franze8b98922014-11-12 15:57:54 +00001194
1195 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
1196 @Override
1197 public void handleMessage(Message msg) {
1198 switch (msg.what) {
1199 case MESSAGE_HEADSET_SERVICE_CONNECTED: {
1200 if (mServiceListener != null) {
1201 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
1202 BluetoothHeadset.this);
1203 }
1204 break;
1205 }
1206 case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
1207 if (mServiceListener != null) {
1208 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
1209 }
Benjamin Franze8b98922014-11-12 15:57:54 +00001210 break;
1211 }
1212 }
1213 }
1214 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215}