blob: 838d3153d54cfbcd6836dfe92d2abf819a803e9e [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
Selim Gurun64bd35e2017-10-17 17:01:38 -070019import android.annotation.RequiresPermission;
Nick Pelly005b2282009-09-10 10:21:56 -070020import android.annotation.SdkConstant;
21import android.annotation.SdkConstant.SdkConstantType;
Selim Gurun64bd35e2017-10-17 17:01:38 -070022import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.ComponentName;
24import android.content.Context;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060025import android.os.Binder;
Benjamin Franze8b98922014-11-12 15:57:54 +000026import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.os.IBinder;
Benjamin Franze8b98922014-11-12 15:57:54 +000028import android.os.Looper;
29import android.os.Message;
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070030import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.util.Log;
32
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -070033import java.util.ArrayList;
34import java.util.List;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070035
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036/**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037 * Public API for controlling the Bluetooth Headset Service. This includes both
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070038 * Bluetooth Headset and Handsfree (v1.5) profiles.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070040 * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 * Service via IPC.
42 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070043 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
44 * the BluetoothHeadset proxy object. Use
45 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 *
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070047 * <p> Android only supports one connected Bluetooth Headset at a time.
48 * Each method is protected with its appropriate permission.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070050public final class BluetoothHeadset implements BluetoothProfile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 private static final String TAG = "BluetoothHeadset";
fredc0f420372012-04-12 00:02:00 -070052 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070053 private static final boolean VDBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Nick Pelly005b2282009-09-10 10:21:56 -070055 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070056 * Intent used to broadcast the change in connection state of the Headset
57 * profile.
58 *
59 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080060 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070061 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
62 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
63 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080064 * </ul>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080065 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070066 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
67 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
68 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080069 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
70 * receive.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070071 */
72 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
73 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070074 "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070075
76 /**
77 * Intent used to broadcast the change in the Audio Connection state of the
78 * A2DP profile.
79 *
80 * <p>This intent will have 3 extras:
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080081 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070082 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
83 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
84 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080085 * </ul>
Jaikumar Ganesh0706fed2011-01-26 11:46:56 -080086 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -070087 * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
88 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -080089 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
90 * to receive.
Nick Pelly005b2282009-09-10 10:21:56 -070091 */
92 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
93 public static final String ACTION_AUDIO_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070094 "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
Nick Pelly005b2282009-09-10 10:21:56 -070095
Jaikumar Ganeshc24dbdb2010-04-02 14:44:43 -070096
Nick Pelly005b2282009-09-10 10:21:56 -070097 /**
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -070098 * Intent used to broadcast that the headset has posted a
99 * vendor-specific event.
100 *
101 * <p>This intent will have 4 extras and 1 category.
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800102 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700103 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
104 * </li>
105 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
106 * specific command </li>
107 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
108 * command type which can be one of {@link #AT_CMD_TYPE_READ},
109 * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
110 * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
111 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
112 * arguments. </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800113 * </ul>
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700114 *
Jack Hea355e5e2017-08-22 16:06:54 -0700115 * <p> The category is the Company ID of the vendor defining the
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700116 * vendor-specific command. {@link BluetoothAssignedNumbers}
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700117 *
118 * For example, for Plantronics specific events
119 * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
120 *
121 * <p> For example, an AT+XEVENT=foo,3 will get translated into
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800122 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700123 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
124 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
125 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800126 * </ul>
127 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
128 * to receive.
Herb Jellineka4733942010-08-10 13:17:43 -0700129 */
130 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
131 public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
132 "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
133
134 /**
135 * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
136 * intents that contains the name of the vendor-specific command.
137 */
138 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
139 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
140
141 /**
142 * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700143 * intents that contains the AT command type of the vendor-specific command.
Herb Jellineka4733942010-08-10 13:17:43 -0700144 */
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700145 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
146 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
147
148 /**
149 * AT command type READ used with
150 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
151 * For example, AT+VGM?. There are no arguments for this command type.
152 */
153 public static final int AT_CMD_TYPE_READ = 0;
154
155 /**
156 * AT command type TEST used with
157 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
158 * For example, AT+VGM=?. There are no arguments for this command type.
159 */
160 public static final int AT_CMD_TYPE_TEST = 1;
161
162 /**
163 * AT command type SET used with
164 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
165 * For example, AT+VGM=<args>.
166 */
167 public static final int AT_CMD_TYPE_SET = 2;
168
169 /**
170 * AT command type BASIC used with
171 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
172 * For example, ATD. Single character commands and everything following the
173 * character are arguments.
174 */
175 public static final int AT_CMD_TYPE_BASIC = 3;
176
177 /**
178 * AT command type ACTION used with
179 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
180 * For example, AT+CHUP. There are no arguments for action commands.
181 */
182 public static final int AT_CMD_TYPE_ACTION = 4;
Herb Jellineka4733942010-08-10 13:17:43 -0700183
184 /**
185 * A Parcelable String array extra field in
186 * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
187 * the arguments to the vendor-specific command.
188 */
189 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
190 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
191
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700192 /**
193 * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
194 * for the companyId
195 */
Jack Hea355e5e2017-08-22 16:06:54 -0700196 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
Jaikumar Ganeshe775b3d2010-09-29 11:34:59 -0700197 "android.bluetooth.headset.intent.category.companyid";
198
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700199 /**
Edward Jee922d41b2013-08-16 04:07:49 -0700200 * A vendor-specific command for unsolicited result code.
201 */
202 public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
203
204 /**
Jack He0fcbba22017-06-20 17:07:40 -0700205 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700206 *
Jack He0fcbba22017-06-20 17:07:40 -0700207 * @hide
208 */
Jack He679d0bc2017-06-20 17:09:47 -0700209 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
210
211 /**
212 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700213 *
Jack He679d0bc2017-06-20 17:09:47 -0700214 * @hide
215 */
216 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
217
218 /**
219 * Battery level indicator associated with
220 * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
Jack Hea355e5e2017-08-22 16:06:54 -0700221 *
Jack He679d0bc2017-06-20 17:09:47 -0700222 * @hide
223 */
224 public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
225
226 /**
227 * A vendor-specific AT command
Jack Hea355e5e2017-08-22 16:06:54 -0700228 *
Jack He679d0bc2017-06-20 17:09:47 -0700229 * @hide
230 */
Jack He0fcbba22017-06-20 17:07:40 -0700231 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
232
233 /**
234 * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
Jack Hea355e5e2017-08-22 16:06:54 -0700235 *
Jack He0fcbba22017-06-20 17:07:40 -0700236 * @hide
237 */
238 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
239
240 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800241 * Headset state when SCO audio is not connected.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700242 * This state can be one of
243 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
244 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
245 */
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800246 public static final int STATE_AUDIO_DISCONNECTED = 10;
Herb Jellineka4733942010-08-10 13:17:43 -0700247
248 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800249 * Headset state when SCO audio is connecting.
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700250 * This state can be one of
251 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
252 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700253 */
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800254 public static final int STATE_AUDIO_CONNECTING = 11;
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700255
256 /**
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800257 * Headset state when SCO audio is connected.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700258 * This state can be one of
259 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
260 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Nick Pelly005b2282009-09-10 10:21:56 -0700261 */
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800262
263 /**
264 * Intent used to broadcast the headset's indicator status
265 *
266 * <p>This intent will have 3 extras:
267 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700268 * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
269 * is supported by the headset ( as indicated by AT+BIND command in the SLC
270 * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
271 * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
272 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800273 * </ul>
Jack He73795442017-06-22 12:56:54 -0700274 * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
Jack Hea355e5e2017-08-22 16:06:54 -0700275 * are given an assigned number. Below shows the assigned number of Indicator added so far
Jack He73795442017-06-22 12:56:54 -0700276 * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
277 * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
278 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
Jack Hea355e5e2017-08-22 16:06:54 -0700279 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800280 * @hide
281 */
282 public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
283 "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
284
285 /**
Jack He73795442017-06-22 12:56:54 -0700286 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
287 * intents that contains the assigned number of the headset indicator as defined by
288 * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
Jack Hea355e5e2017-08-22 16:06:54 -0700289 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800290 * @hide
291 */
292 public static final String EXTRA_HF_INDICATORS_IND_ID =
293 "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
294
295 /**
Jack He73795442017-06-22 12:56:54 -0700296 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800297 * intents that contains the value of the Headset indicator that is being sent.
Jack Hea355e5e2017-08-22 16:06:54 -0700298 *
Mudumba Ananth0d98ebf2016-02-29 02:14:36 -0800299 * @hide
300 */
301 public static final String EXTRA_HF_INDICATORS_IND_VALUE =
302 "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
303
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800304 public static final int STATE_AUDIO_CONNECTED = 12;
305
Benjamin Franze8b98922014-11-12 15:57:54 +0000306 private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
307 private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700308
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700309 private Context mContext;
310 private ServiceListener mServiceListener;
Jack He16eeac32017-08-17 12:11:18 -0700311 private volatile IBluetoothHeadset mService;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700312 private BluetoothAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313
Jack He2992cd02017-08-22 21:21:23 -0700314 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc0f420372012-04-12 00:02:00 -0700315 new IBluetoothStateChangeCallback.Stub() {
316 public void onBluetoothStateChange(boolean up) {
317 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
318 if (!up) {
Jack Hea355e5e2017-08-22 16:06:54 -0700319 if (VDBG) Log.d(TAG, "Unbinding service...");
Benjamin Franze8b98922014-11-12 15:57:54 +0000320 doUnbind();
fredc0f420372012-04-12 00:02:00 -0700321 } else {
322 synchronized (mConnection) {
323 try {
324 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700325 if (VDBG) Log.d(TAG, "Binding service...");
Dianne Hackborn221ea892013-08-04 16:50:16 -0700326 doBind();
fredc0f420372012-04-12 00:02:00 -0700327 }
328 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700329 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700330 }
331 }
332 }
333 }
Jack Hea355e5e2017-08-22 16:06:54 -0700334 };
fredc0f420372012-04-12 00:02:00 -0700335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 /**
337 * Create a BluetoothHeadset proxy object.
338 */
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700339 /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 mContext = context;
341 mServiceListener = l;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700342 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700343
344 IBluetoothManager mgr = mAdapter.getBluetoothManager();
345 if (mgr != null) {
346 try {
347 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
348 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700349 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700350 }
351 }
352
Dianne Hackborn221ea892013-08-04 16:50:16 -0700353 doBind();
354 }
355
356 boolean doBind() {
Benjamin Franze8b98922014-11-12 15:57:54 +0000357 try {
358 return mAdapter.getBluetoothManager().bindBluetoothProfileService(
359 BluetoothProfile.HEADSET, mConnection);
360 } catch (RemoteException e) {
361 Log.e(TAG, "Unable to bind HeadsetService", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
Benjamin Franze8b98922014-11-12 15:57:54 +0000363 return false;
364 }
365
366 void doUnbind() {
367 synchronized (mConnection) {
368 if (mService != null) {
369 try {
370 mAdapter.getBluetoothManager().unbindBluetoothProfileService(
371 BluetoothProfile.HEADSET, mConnection);
372 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700373 Log.e(TAG, "Unable to unbind HeadsetService", e);
Benjamin Franze8b98922014-11-12 15:57:54 +0000374 }
375 }
376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 }
378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 /**
380 * Close the connection to the backing service.
381 * Other public functions of BluetoothHeadset will return default error
382 * results once close() has been called. Multiple invocations of close()
383 * are ok.
384 */
Matthew Xie13450df2012-03-22 17:18:37 -0700385 /*package*/ void close() {
Matthew Xie563e4142012-10-09 22:10:37 -0700386 if (VDBG) log("close()");
fredc0f420372012-04-12 00:02:00 -0700387
388 IBluetoothManager mgr = mAdapter.getBluetoothManager();
389 if (mgr != null) {
390 try {
391 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
392 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700393 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700394 }
395 }
Benjamin Franzc88b6bd2014-12-16 15:33:03 +0000396 mServiceListener = null;
Benjamin Franze8b98922014-11-12 15:57:54 +0000397 doUnbind();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 }
399
400 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700401 * Initiate connection to a profile of the remote bluetooth device.
402 *
403 * <p> Currently, the system supports only 1 connection to the
404 * headset/handsfree profile. The API will automatically disconnect connected
405 * devices before connecting.
406 *
407 * <p> This API returns false in scenarios like the profile on the
408 * device is already connected or Bluetooth is not turned on.
409 * When this API returns true, it is guaranteed that
410 * connection state intent for the profile will be broadcasted with
411 * the state. Users can get the connection state of the profile
412 * from this intent.
413 *
414 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
415 * permission.
416 *
417 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700418 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700419 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 */
Selim Gurun64bd35e2017-10-17 17:01:38 -0700421 @SystemApi
422 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700423 public boolean connect(BluetoothDevice device) {
424 if (DBG) log("connect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700425 final IBluetoothHeadset service = mService;
426 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 try {
Jack He16eeac32017-08-17 12:11:18 -0700428 return service.connect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700429 } catch (RemoteException e) {
430 Log.e(TAG, Log.getStackTraceString(new Throwable()));
431 return false;
432 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 }
Jack He16eeac32017-08-17 12:11:18 -0700434 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 return false;
436 }
437
438 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700439 * Initiate disconnection from a profile
440 *
441 * <p> This API will return false in scenarios like the profile on the
442 * Bluetooth device is not in connected state etc. When this API returns,
443 * true, it is guaranteed that the connection state change
444 * intent will be broadcasted with the state. Users can get the
445 * disconnection state of the profile from this intent.
446 *
447 * <p> If the disconnection is initiated by a remote device, the state
448 * will transition from {@link #STATE_CONNECTED} to
449 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
450 * host (local) device the state will transition from
451 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
452 * state {@link #STATE_DISCONNECTED}. The transition to
453 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
454 * two scenarios.
455 *
456 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
457 * permission.
458 *
459 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700460 * @return false on immediate error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700461 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
Selim Gurun64bd35e2017-10-17 17:01:38 -0700463 @SystemApi
464 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700465 public boolean disconnect(BluetoothDevice device) {
466 if (DBG) log("disconnect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700467 final IBluetoothHeadset service = mService;
468 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 try {
Jack He16eeac32017-08-17 12:11:18 -0700470 return service.disconnect(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700471 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700472 Log.e(TAG, Log.getStackTraceString(new Throwable()));
473 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700474 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 }
Jack He16eeac32017-08-17 12:11:18 -0700476 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 return false;
478 }
479
480 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700481 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 */
Jack He2992cd02017-08-22 21:21:23 -0700483 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700484 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700485 if (VDBG) log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700486 final IBluetoothHeadset service = mService;
487 if (service != null && isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 try {
Jack He16eeac32017-08-17 12:11:18 -0700489 return service.getConnectedDevices();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700490 } catch (RemoteException e) {
491 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700492 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700493 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 }
Jack He16eeac32017-08-17 12:11:18 -0700495 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700496 return new ArrayList<BluetoothDevice>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 }
498
499 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700500 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 */
Jack He2992cd02017-08-22 21:21:23 -0700502 @Override
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700503 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700504 if (VDBG) log("getDevicesMatchingStates()");
Jack He16eeac32017-08-17 12:11:18 -0700505 final IBluetoothHeadset service = mService;
506 if (service != null && isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 try {
Jack He16eeac32017-08-17 12:11:18 -0700508 return service.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700509 } catch (RemoteException e) {
510 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700511 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700512 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 }
Jack He16eeac32017-08-17 12:11:18 -0700514 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh03cd78c2010-10-18 16:41:53 -0700515 return new ArrayList<BluetoothDevice>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
517
518 /**
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700519 * {@inheritDoc}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 */
Jack He2992cd02017-08-22 21:21:23 -0700521 @Override
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700522 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700523 if (VDBG) log("getConnectionState(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700524 final IBluetoothHeadset service = mService;
525 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 try {
Jack He16eeac32017-08-17 12:11:18 -0700527 return service.getConnectionState(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700528 } catch (RemoteException e) {
529 Log.e(TAG, Log.getStackTraceString(new Throwable()));
530 return BluetoothProfile.STATE_DISCONNECTED;
531 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 }
Jack He16eeac32017-08-17 12:11:18 -0700533 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700534 return BluetoothProfile.STATE_DISCONNECTED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 }
536
537 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700538 * Set priority of the profile
539 *
540 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700541 * Priority can be one of {@link #PRIORITY_ON} or
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700542 * {@link #PRIORITY_OFF},
543 *
544 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
545 * permission.
546 *
547 * @param device Paired bluetooth device
548 * @param priority
549 * @return true if priority is set, false on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700550 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 */
Selim Gurun64bd35e2017-10-17 17:01:38 -0700552 @SystemApi
553 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
Nick Pellybd022f42009-08-14 18:33:38 -0700554 public boolean setPriority(BluetoothDevice device, int priority) {
555 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Jack He16eeac32017-08-17 12:11:18 -0700556 final IBluetoothHeadset service = mService;
557 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700558 if (priority != BluetoothProfile.PRIORITY_OFF
559 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700560 return false;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700561 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 try {
Jack He16eeac32017-08-17 12:11:18 -0700563 return service.setPriority(device, priority);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700564 } catch (RemoteException e) {
565 Log.e(TAG, Log.getStackTraceString(new Throwable()));
566 return false;
567 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 }
Jack He16eeac32017-08-17 12:11:18 -0700569 if (service == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 return false;
571 }
572
573 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700574 * Get the priority of the profile.
575 *
576 * <p> The priority can be any of:
577 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
578 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
579 *
580 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
581 *
582 * @param device Bluetooth device
583 * @return priority of the device
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700584 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 */
Nick Pellybd022f42009-08-14 18:33:38 -0700586 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700587 if (VDBG) log("getPriority(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700588 final IBluetoothHeadset service = mService;
589 if (service != null && isEnabled() && isValidDevice(device)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 try {
Jack He16eeac32017-08-17 12:11:18 -0700591 return service.getPriority(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700592 } catch (RemoteException e) {
593 Log.e(TAG, Log.getStackTraceString(new Throwable()));
594 return PRIORITY_OFF;
595 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 }
Jack He16eeac32017-08-17 12:11:18 -0700597 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700598 return PRIORITY_OFF;
599 }
600
601 /**
602 * Start Bluetooth voice recognition. This methods sends the voice
603 * recognition AT command to the headset and establishes the
604 * audio connection.
605 *
606 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800607 * If this function returns true, this intent will be broadcasted with
608 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700609 *
Jaikumar Ganeshb0a1d012010-11-11 10:49:46 -0800610 * <p> {@link #EXTRA_STATE} will transition from
611 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
612 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
613 * in case of failure to establish the audio connection.
614 *
615 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700616 *
617 * @param device Bluetooth headset
Jack Hea355e5e2017-08-22 16:06:54 -0700618 * @return false if there is no headset connected of if the connected headset doesn't support
619 * voice recognition or on error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700620 */
621 public boolean startVoiceRecognition(BluetoothDevice device) {
622 if (DBG) log("startVoiceRecognition()");
Jack He16eeac32017-08-17 12:11:18 -0700623 final IBluetoothHeadset service = mService;
624 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700625 try {
Jack He16eeac32017-08-17 12:11:18 -0700626 return service.startVoiceRecognition(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700627 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700628 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700629 }
630 }
Jack He16eeac32017-08-17 12:11:18 -0700631 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700632 return false;
633 }
634
635 /**
636 * Stop Bluetooth Voice Recognition mode, and shut down the
637 * Bluetooth audio path.
638 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800639 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700640 *
641 * @param device Bluetooth headset
Jack Hea355e5e2017-08-22 16:06:54 -0700642 * @return false if there is no headset connected or on error, true otherwise
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700643 */
644 public boolean stopVoiceRecognition(BluetoothDevice device) {
645 if (DBG) log("stopVoiceRecognition()");
Jack He16eeac32017-08-17 12:11:18 -0700646 final IBluetoothHeadset service = mService;
647 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700648 try {
Jack He16eeac32017-08-17 12:11:18 -0700649 return service.stopVoiceRecognition(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700650 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700651 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700652 }
653 }
Jack He16eeac32017-08-17 12:11:18 -0700654 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700655 return false;
656 }
657
658 /**
659 * Check if Bluetooth SCO audio is connected.
660 *
Jaikumar Ganeshc8fa4ff2011-01-25 16:03:13 -0800661 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700662 *
663 * @param device Bluetooth headset
Jack Hea355e5e2017-08-22 16:06:54 -0700664 * @return true if SCO is connected, false otherwise or on error
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700665 */
666 public boolean isAudioConnected(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700667 if (VDBG) log("isAudioConnected()");
Jack He16eeac32017-08-17 12:11:18 -0700668 final IBluetoothHeadset service = mService;
669 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700670 try {
Jack He16eeac32017-08-17 12:11:18 -0700671 return service.isAudioConnected(device);
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700672 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700673 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700674 }
675 }
Jack He16eeac32017-08-17 12:11:18 -0700676 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700677 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 }
679
680 /**
Eric Laurentd726b352010-03-17 14:59:27 -0700681 * Indicates if current platform supports voice dialing over bluetooth SCO.
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -0700682 *
Eric Laurentd726b352010-03-17 14:59:27 -0700683 * @return true if voice dialing over bluetooth is supported, false otherwise.
684 * @hide
685 */
686 public static boolean isBluetoothVoiceDialingEnabled(Context context) {
687 return context.getResources().getBoolean(
688 com.android.internal.R.bool.config_bluetooth_sco_off_call);
689 }
690
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700691 /**
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700692 * Get the current audio state of the Headset.
693 * Note: This is an internal function and shouldn't be exposed
694 *
695 * @hide
696 */
697 public int getAudioState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700698 if (VDBG) log("getAudioState");
Jack He16eeac32017-08-17 12:11:18 -0700699 final IBluetoothHeadset service = mService;
700 if (service != null && !isDisabled()) {
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700701 try {
Jack He16eeac32017-08-17 12:11:18 -0700702 return service.getAudioState(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700703 } catch (RemoteException e) {
704 Log.e(TAG, e.toString());
705 }
Jaikumar Ganesh30d18162010-11-01 11:59:57 -0700706 } else {
707 Log.w(TAG, "Proxy not attached to service");
708 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
709 }
710 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
711 }
712
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700713 /**
Bryce Lee0db53d92015-11-16 08:55:52 -0800714 * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
715 * audio to the HF unless explicitly told to.
716 * This method should be used in cases where the SCO channel is shared between multiple profiles
717 * and must be delegated by a source knowledgeable
718 * Note: This is an internal function and shouldn't be exposed
719 *
720 * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
Bryce Lee0db53d92015-11-16 08:55:52 -0800721 * @hide
722 */
723 public void setAudioRouteAllowed(boolean allowed) {
724 if (VDBG) log("setAudioRouteAllowed");
Jack He16eeac32017-08-17 12:11:18 -0700725 final IBluetoothHeadset service = mService;
726 if (service != null && isEnabled()) {
Bryce Lee0db53d92015-11-16 08:55:52 -0800727 try {
Jack He16eeac32017-08-17 12:11:18 -0700728 service.setAudioRouteAllowed(allowed);
Jack Hea355e5e2017-08-22 16:06:54 -0700729 } catch (RemoteException e) {
730 Log.e(TAG, e.toString());
731 }
Bryce Lee0db53d92015-11-16 08:55:52 -0800732 } else {
733 Log.w(TAG, "Proxy not attached to service");
734 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
735 }
736 }
737
738 /**
739 * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
740 * Note: This is an internal function and shouldn't be exposed
741 *
742 * @hide
743 */
744 public boolean getAudioRouteAllowed() {
745 if (VDBG) log("getAudioRouteAllowed");
Jack He16eeac32017-08-17 12:11:18 -0700746 final IBluetoothHeadset service = mService;
747 if (service != null && isEnabled()) {
Bryce Lee0db53d92015-11-16 08:55:52 -0800748 try {
Jack He16eeac32017-08-17 12:11:18 -0700749 return service.getAudioRouteAllowed();
Jack Hea355e5e2017-08-22 16:06:54 -0700750 } catch (RemoteException e) {
751 Log.e(TAG, e.toString());
752 }
Bryce Lee0db53d92015-11-16 08:55:52 -0800753 } else {
754 Log.w(TAG, "Proxy not attached to service");
755 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
756 }
757 return false;
758 }
759
760 /**
Jack He1dd1e032017-05-09 17:16:01 -0700761 * Force SCO audio to be opened regardless any other restrictions
762 *
Jack Hea355e5e2017-08-22 16:06:54 -0700763 * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio
764 * False to use SCO audio in normal manner
Jack He1dd1e032017-05-09 17:16:01 -0700765 * @hide
766 */
767 public void setForceScoAudio(boolean forced) {
768 if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
Jack He16eeac32017-08-17 12:11:18 -0700769 final IBluetoothHeadset service = mService;
770 if (service != null && isEnabled()) {
Jack He1dd1e032017-05-09 17:16:01 -0700771 try {
Jack He16eeac32017-08-17 12:11:18 -0700772 service.setForceScoAudio(forced);
Jack He1dd1e032017-05-09 17:16:01 -0700773 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700774 Log.e(TAG, e.toString());
Jack He1dd1e032017-05-09 17:16:01 -0700775 }
776 } else {
777 Log.w(TAG, "Proxy not attached to service");
778 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
779 }
780 }
781
782 /**
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800783 * Check if Bluetooth SCO audio is connected.
784 *
785 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
786 *
Jack Hea355e5e2017-08-22 16:06:54 -0700787 * @return true if SCO is connected, false otherwise or on error
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800788 * @hide
789 */
790 public boolean isAudioOn() {
Matthew Xie563e4142012-10-09 22:10:37 -0700791 if (VDBG) log("isAudioOn()");
Jack He16eeac32017-08-17 12:11:18 -0700792 final IBluetoothHeadset service = mService;
793 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800794 try {
Jack He16eeac32017-08-17 12:11:18 -0700795 return service.isAudioOn();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800796 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700797 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800798 }
799 }
Jack He16eeac32017-08-17 12:11:18 -0700800 if (service == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800801 return false;
802
803 }
804
805 /**
806 * Initiates a connection of headset audio.
807 * It setup SCO channel with remote connected headset device.
808 *
Jack Hea355e5e2017-08-22 16:06:54 -0700809 * @return true if successful false if there was some error such as there is no connected
810 * headset
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800811 * @hide
812 */
813 public boolean connectAudio() {
Jack He16eeac32017-08-17 12:11:18 -0700814 final IBluetoothHeadset service = mService;
815 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800816 try {
Jack He16eeac32017-08-17 12:11:18 -0700817 return service.connectAudio();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800818 } catch (RemoteException e) {
819 Log.e(TAG, e.toString());
820 }
821 } else {
822 Log.w(TAG, "Proxy not attached to service");
823 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
824 }
825 return false;
826 }
827
828 /**
829 * Initiates a disconnection of headset audio.
830 * It tears down the SCO channel from remote headset device.
831 *
Jack Hea355e5e2017-08-22 16:06:54 -0700832 * @return true if successful false if there was some error such as there is no connected SCO
833 * channel
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800834 * @hide
835 */
836 public boolean disconnectAudio() {
Jack He16eeac32017-08-17 12:11:18 -0700837 final IBluetoothHeadset service = mService;
838 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800839 try {
Jack He16eeac32017-08-17 12:11:18 -0700840 return service.disconnectAudio();
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800841 } catch (RemoteException e) {
842 Log.e(TAG, e.toString());
843 }
844 } else {
845 Log.w(TAG, "Proxy not attached to service");
846 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
847 }
848 return false;
849 }
850
851 /**
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800852 * Initiates a SCO channel connection with the headset (if connected).
853 * Also initiates a virtual voice call for Handsfree devices as many devices
854 * do not accept SCO audio without a call.
855 * This API allows the handsfree device to be used for routing non-cellular
856 * call audio.
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700857 *
858 * @param device Remote Bluetooth Device
859 * @return true if successful, false if there was some error.
860 * @hide
861 */
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800862 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
863 if (DBG) log("startScoUsingVirtualVoiceCall()");
Jack He16eeac32017-08-17 12:11:18 -0700864 final IBluetoothHeadset service = mService;
865 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700866 try {
Jack He16eeac32017-08-17 12:11:18 -0700867 return service.startScoUsingVirtualVoiceCall(device);
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700868 } catch (RemoteException e) {
869 Log.e(TAG, e.toString());
870 }
871 } else {
872 Log.w(TAG, "Proxy not attached to service");
873 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
874 }
875 return false;
876 }
877
878 /**
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800879 * Terminates an ongoing SCO connection and the associated virtual
880 * call.
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700881 *
882 * @param device Remote Bluetooth Device
883 * @return true if successful, false if there was some error.
884 * @hide
885 */
Jaikumar Ganeshdde68c62011-01-24 13:55:27 -0800886 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
887 if (DBG) log("stopScoUsingVirtualVoiceCall()");
Jack He16eeac32017-08-17 12:11:18 -0700888 final IBluetoothHeadset service = mService;
889 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700890 try {
Jack He16eeac32017-08-17 12:11:18 -0700891 return service.stopScoUsingVirtualVoiceCall(device);
Jaikumar Ganeshf2e6b132010-10-26 17:10:09 -0700892 } catch (RemoteException e) {
893 Log.e(TAG, e.toString());
894 }
895 } else {
896 Log.w(TAG, "Proxy not attached to service");
897 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
898 }
899 return false;
900 }
901
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800902 /**
903 * Notify Headset of phone state change.
904 * This is a backdoor for phone app to call BluetoothHeadset since
905 * there is currently not a good way to get precise call state change outside
906 * of phone app.
907 *
908 * @hide
909 */
910 public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
Jack Hea355e5e2017-08-22 16:06:54 -0700911 int type) {
Jack He16eeac32017-08-17 12:11:18 -0700912 final IBluetoothHeadset service = mService;
913 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800914 try {
Jack He16eeac32017-08-17 12:11:18 -0700915 service.phoneStateChanged(numActive, numHeld, callState, number, type);
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800916 } catch (RemoteException e) {
917 Log.e(TAG, e.toString());
918 }
919 } else {
920 Log.w(TAG, "Proxy not attached to service");
921 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
922 }
923 }
924
925 /**
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800926 * Send Headset of CLCC response
927 *
928 * @hide
929 */
930 public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
Jack Hea355e5e2017-08-22 16:06:54 -0700931 String number, int type) {
Jack He16eeac32017-08-17 12:11:18 -0700932 final IBluetoothHeadset service = mService;
933 if (service != null && isEnabled()) {
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800934 try {
Jack He16eeac32017-08-17 12:11:18 -0700935 service.clccResponse(index, direction, status, mode, mpty, number, type);
Matthew Xie3e8c82e2012-02-16 16:57:18 -0800936 } catch (RemoteException e) {
937 Log.e(TAG, e.toString());
938 }
939 } else {
940 Log.w(TAG, "Proxy not attached to service");
941 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
942 }
943 }
944
Edward Jee922d41b2013-08-16 04:07:49 -0700945 /**
946 * Sends a vendor-specific unsolicited result code to the headset.
947 *
Jack Hea355e5e2017-08-22 16:06:54 -0700948 * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code
949 * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the
950 * string <code>"+ANDROID: 0"</code> will be sent.
Edward Jee922d41b2013-08-16 04:07:49 -0700951 *
Ying Wangb2405782013-08-26 17:48:22 -0700952 * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
Edward Jee922d41b2013-08-16 04:07:49 -0700953 *
954 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
955 *
956 * @param device Bluetooth headset.
957 * @param command A vendor-specific command.
958 * @param arg The argument that will be attached to the command.
959 * @return {@code false} if there is no headset connected, or if the command is not an allowed
Jack Hea355e5e2017-08-22 16:06:54 -0700960 * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
Edward Jee922d41b2013-08-16 04:07:49 -0700961 * @throws IllegalArgumentException if {@code command} is {@code null}.
962 */
963 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
964 String arg) {
965 if (DBG) {
966 log("sendVendorSpecificResultCode()");
967 }
968 if (command == null) {
969 throw new IllegalArgumentException("command is null");
970 }
Jack He16eeac32017-08-17 12:11:18 -0700971 final IBluetoothHeadset service = mService;
972 if (service != null && isEnabled() && isValidDevice(device)) {
Edward Jee922d41b2013-08-16 04:07:49 -0700973 try {
Jack He16eeac32017-08-17 12:11:18 -0700974 return service.sendVendorSpecificResultCode(device, command, arg);
Edward Jee922d41b2013-08-16 04:07:49 -0700975 } catch (RemoteException e) {
976 Log.e(TAG, Log.getStackTraceString(new Throwable()));
977 }
978 }
Jack He16eeac32017-08-17 12:11:18 -0700979 if (service == null) {
Edward Jee922d41b2013-08-16 04:07:49 -0700980 Log.w(TAG, "Proxy not attached to service");
981 }
982 return false;
983 }
984
Mudumba Ananth177d0782014-04-27 13:11:00 -0700985 /**
Jack Hee86bdca2016-11-17 16:19:43 -0800986 * check if in-band ringing is supported for this platform.
987 *
Jack Hea355e5e2017-08-22 16:06:54 -0700988 * @return true if in-band ringing is supported false if in-band ringing is not supported
Jack Hee86bdca2016-11-17 16:19:43 -0800989 * @hide
990 */
991 public static boolean isInbandRingingSupported(Context context) {
992 return context.getResources().getBoolean(
993 com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
994 }
995
Jack He2992cd02017-08-22 21:21:23 -0700996 private final IBluetoothProfileServiceConnection mConnection =
997 new IBluetoothProfileServiceConnection.Stub() {
Benjamin Franze8b98922014-11-12 15:57:54 +0000998 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 public void onServiceConnected(ComponentName className, IBinder service) {
1000 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -06001001 mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
Benjamin Franze8b98922014-11-12 15:57:54 +00001002 mHandler.sendMessage(mHandler.obtainMessage(
1003 MESSAGE_HEADSET_SERVICE_CONNECTED));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 }
Jack Hea355e5e2017-08-22 16:06:54 -07001005
Benjamin Franze8b98922014-11-12 15:57:54 +00001006 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 public void onServiceDisconnected(ComponentName className) {
1008 if (DBG) Log.d(TAG, "Proxy object disconnected");
1009 mService = null;
Benjamin Franze8b98922014-11-12 15:57:54 +00001010 mHandler.sendMessage(mHandler.obtainMessage(
1011 MESSAGE_HEADSET_SERVICE_DISCONNECTED));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 }
1013 };
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -08001014
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001015 private boolean isEnabled() {
Jack He16eeac32017-08-17 12:11:18 -07001016 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001017 }
1018
Jaikumar Ganesh6f7a9732011-04-06 11:09:30 -07001019 private boolean isDisabled() {
Jack He16eeac32017-08-17 12:11:18 -07001020 return mAdapter.getState() == BluetoothAdapter.STATE_OFF;
Jaikumar Ganesh6f7a9732011-04-06 11:09:30 -07001021 }
1022
Jack He16eeac32017-08-17 12:11:18 -07001023 private static boolean isValidDevice(BluetoothDevice device) {
1024 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Jaikumar Ganesh62c37ef2010-08-24 17:36:13 -07001025 }
1026
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -08001027 private static void log(String msg) {
1028 Log.d(TAG, msg);
1029 }
Benjamin Franze8b98922014-11-12 15:57:54 +00001030
1031 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
1032 @Override
1033 public void handleMessage(Message msg) {
1034 switch (msg.what) {
1035 case MESSAGE_HEADSET_SERVICE_CONNECTED: {
1036 if (mServiceListener != null) {
1037 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
1038 BluetoothHeadset.this);
1039 }
1040 break;
1041 }
1042 case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
1043 if (mServiceListener != null) {
1044 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
1045 }
Benjamin Franze8b98922014-11-12 15:57:54 +00001046 break;
1047 }
1048 }
1049 }
1050 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051}