blob: 132f383997b2b280da14818e5193bc07b9235cb2 [file] [log] [blame]
The Android Open Source Project33897762009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Nick Pellydac4c0d2009-09-10 10:21:56 -070019import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
The Android Open Source Project33897762009-03-03 19:31:44 -080021import android.content.ComponentName;
22import android.content.Context;
Jeff Sharkey73458a82016-11-04 11:23:46 -060023import android.os.Binder;
Benjamin Franz3362fef2014-11-12 15:57:54 +000024import android.os.Handler;
The Android Open Source Project33897762009-03-03 19:31:44 -080025import android.os.IBinder;
Benjamin Franz3362fef2014-11-12 15:57:54 +000026import android.os.Looper;
27import android.os.Message;
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -070028import android.os.RemoteException;
The Android Open Source Project33897762009-03-03 19:31:44 -080029import android.util.Log;
30
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -070031import java.util.ArrayList;
32import java.util.List;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070033
The Android Open Source Project33897762009-03-03 19:31:44 -080034/**
The Android Open Source Project33897762009-03-03 19:31:44 -080035 * Public API for controlling the Bluetooth Headset Service. This includes both
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070036 * Bluetooth Headset and Handsfree (v1.5) profiles.
The Android Open Source Project33897762009-03-03 19:31:44 -080037 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070038 * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
The Android Open Source Project33897762009-03-03 19:31:44 -080039 * Service via IPC.
40 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070041 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
42 * the BluetoothHeadset proxy object. Use
43 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
The Android Open Source Project33897762009-03-03 19:31:44 -080044 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070045 * <p> Android only supports one connected Bluetooth Headset at a time.
46 * Each method is protected with its appropriate permission.
The Android Open Source Project33897762009-03-03 19:31:44 -080047 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070048public final class BluetoothHeadset implements BluetoothProfile {
The Android Open Source Project33897762009-03-03 19:31:44 -080049 private static final String TAG = "BluetoothHeadset";
fredc3c719642012-04-12 00:02:00 -070050 private static final boolean DBG = true;
Matthew Xief8035a72012-10-09 22:10:37 -070051 private static final boolean VDBG = false;
The Android Open Source Project33897762009-03-03 19:31:44 -080052
Nick Pellydac4c0d2009-09-10 10:21:56 -070053 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070054 * Intent used to broadcast the change in connection state of the Headset
55 * profile.
56 *
57 * <p>This intent will have 3 extras:
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080058 * <ul>
Jack He910201b2017-08-22 16:06:54 -070059 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
60 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
61 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080062 * </ul>
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -080063 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070064 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
65 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
66 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080067 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
68 * receive.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070069 */
70 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
71 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack He910201b2017-08-22 16:06:54 -070072 "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070073
74 /**
75 * Intent used to broadcast the change in the Audio Connection state of the
76 * A2DP profile.
77 *
78 * <p>This intent will have 3 extras:
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080079 * <ul>
Jack He910201b2017-08-22 16:06:54 -070080 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
81 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
82 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080083 * </ul>
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -080084 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070085 * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
86 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080087 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
88 * to receive.
Nick Pellydac4c0d2009-09-10 10:21:56 -070089 */
90 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
91 public static final String ACTION_AUDIO_STATE_CHANGED =
Jack He910201b2017-08-22 16:06:54 -070092 "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
Nick Pellydac4c0d2009-09-10 10:21:56 -070093
Jaikumar Ganeshf48cda52010-04-02 14:44:43 -070094
Nick Pellydac4c0d2009-09-10 10:21:56 -070095 /**
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -070096 * Intent used to broadcast that the headset has posted a
97 * vendor-specific event.
98 *
99 * <p>This intent will have 4 extras and 1 category.
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800100 * <ul>
Jack He910201b2017-08-22 16:06:54 -0700101 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
102 * </li>
103 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
104 * specific command </li>
105 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
106 * command type which can be one of {@link #AT_CMD_TYPE_READ},
107 * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
108 * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
109 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
110 * arguments. </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800111 * </ul>
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700112 *
Jack He910201b2017-08-22 16:06:54 -0700113 * <p> The category is the Company ID of the vendor defining the
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700114 * vendor-specific command. {@link BluetoothAssignedNumbers}
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700115 *
116 * For example, for Plantronics specific events
117 * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
118 *
119 * <p> For example, an AT+XEVENT=foo,3 will get translated into
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800120 * <ul>
Jack He910201b2017-08-22 16:06:54 -0700121 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
122 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
123 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800124 * </ul>
125 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
126 * to receive.
Herb Jellinekaad41c52010-08-10 13:17:43 -0700127 */
128 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
129 public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
130 "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
131
132 /**
133 * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
134 * intents that contains the name of the vendor-specific command.
135 */
136 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
137 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
138
139 /**
140 * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700141 * intents that contains the AT command type of the vendor-specific command.
Herb Jellinekaad41c52010-08-10 13:17:43 -0700142 */
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700143 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
144 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
145
146 /**
147 * AT command type READ used with
148 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
149 * For example, AT+VGM?. There are no arguments for this command type.
150 */
151 public static final int AT_CMD_TYPE_READ = 0;
152
153 /**
154 * AT command type TEST used with
155 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
156 * For example, AT+VGM=?. There are no arguments for this command type.
157 */
158 public static final int AT_CMD_TYPE_TEST = 1;
159
160 /**
161 * AT command type SET used with
162 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
163 * For example, AT+VGM=<args>.
164 */
165 public static final int AT_CMD_TYPE_SET = 2;
166
167 /**
168 * AT command type BASIC used with
169 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
170 * For example, ATD. Single character commands and everything following the
171 * character are arguments.
172 */
173 public static final int AT_CMD_TYPE_BASIC = 3;
174
175 /**
176 * AT command type ACTION used with
177 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
178 * For example, AT+CHUP. There are no arguments for action commands.
179 */
180 public static final int AT_CMD_TYPE_ACTION = 4;
Herb Jellinekaad41c52010-08-10 13:17:43 -0700181
182 /**
183 * A Parcelable String array extra field in
184 * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
185 * the arguments to the vendor-specific command.
186 */
187 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
188 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
189
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700190 /**
191 * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
192 * for the companyId
193 */
Jack He910201b2017-08-22 16:06:54 -0700194 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700195 "android.bluetooth.headset.intent.category.companyid";
196
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700197 /**
Edward Jee7c81f1f2013-08-16 04:07:49 -0700198 * A vendor-specific command for unsolicited result code.
199 */
200 public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
201
202 /**
Jack He7f9c70b2017-06-20 17:07:40 -0700203 * A vendor-specific AT command
Jack He910201b2017-08-22 16:06:54 -0700204 *
Jack He7f9c70b2017-06-20 17:07:40 -0700205 * @hide
206 */
Jack He0dfd69b2017-06-20 17:09:47 -0700207 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
208
209 /**
210 * A vendor-specific AT command
Jack He910201b2017-08-22 16:06:54 -0700211 *
Jack He0dfd69b2017-06-20 17:09:47 -0700212 * @hide
213 */
214 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
215
216 /**
217 * Battery level indicator associated with
218 * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
Jack He910201b2017-08-22 16:06:54 -0700219 *
Jack He0dfd69b2017-06-20 17:09:47 -0700220 * @hide
221 */
222 public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
223
224 /**
225 * A vendor-specific AT command
Jack He910201b2017-08-22 16:06:54 -0700226 *
Jack He0dfd69b2017-06-20 17:09:47 -0700227 * @hide
228 */
Jack He7f9c70b2017-06-20 17:07:40 -0700229 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
230
231 /**
232 * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
Jack He910201b2017-08-22 16:06:54 -0700233 *
Jack He7f9c70b2017-06-20 17:07:40 -0700234 * @hide
235 */
236 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
237
238 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800239 * Headset state when SCO audio is not connected.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700240 * This state can be one of
241 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
242 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
243 */
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800244 public static final int STATE_AUDIO_DISCONNECTED = 10;
Herb Jellinekaad41c52010-08-10 13:17:43 -0700245
246 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800247 * Headset state when SCO audio is connecting.
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700248 * This state can be one of
249 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
250 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700251 */
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800252 public static final int STATE_AUDIO_CONNECTING = 11;
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700253
254 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800255 * Headset state when SCO audio is connected.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700256 * This state can be one of
257 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
258 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Nick Pellydac4c0d2009-09-10 10:21:56 -0700259 */
Mudumba Ananth3246de62016-02-29 02:14:36 -0800260
261 /**
262 * Intent used to broadcast the headset's indicator status
263 *
264 * <p>This intent will have 3 extras:
265 * <ul>
Jack He910201b2017-08-22 16:06:54 -0700266 * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
267 * is supported by the headset ( as indicated by AT+BIND command in the SLC
268 * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
269 * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
270 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
Mudumba Ananth3246de62016-02-29 02:14:36 -0800271 * </ul>
Jack He48f6a8a2017-06-22 12:56:54 -0700272 * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
Jack He910201b2017-08-22 16:06:54 -0700273 * are given an assigned number. Below shows the assigned number of Indicator added so far
Jack He48f6a8a2017-06-22 12:56:54 -0700274 * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
275 * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
276 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
Jack He910201b2017-08-22 16:06:54 -0700277 *
Mudumba Ananth3246de62016-02-29 02:14:36 -0800278 * @hide
279 */
280 public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
281 "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
282
283 /**
Jack He48f6a8a2017-06-22 12:56:54 -0700284 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
285 * intents that contains the assigned number of the headset indicator as defined by
286 * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
Jack He910201b2017-08-22 16:06:54 -0700287 *
Mudumba Ananth3246de62016-02-29 02:14:36 -0800288 * @hide
289 */
290 public static final String EXTRA_HF_INDICATORS_IND_ID =
291 "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
292
293 /**
Jack He48f6a8a2017-06-22 12:56:54 -0700294 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
Mudumba Ananth3246de62016-02-29 02:14:36 -0800295 * intents that contains the value of the Headset indicator that is being sent.
Jack He910201b2017-08-22 16:06:54 -0700296 *
Mudumba Ananth3246de62016-02-29 02:14:36 -0800297 * @hide
298 */
299 public static final String EXTRA_HF_INDICATORS_IND_VALUE =
300 "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
301
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800302 public static final int STATE_AUDIO_CONNECTED = 12;
303
Benjamin Franz3362fef2014-11-12 15:57:54 +0000304 private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
305 private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700306
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700307 private Context mContext;
308 private ServiceListener mServiceListener;
The Android Open Source Project33897762009-03-03 19:31:44 -0800309 private IBluetoothHeadset mService;
Matthew Xie5e9fb032012-03-21 23:15:06 -0700310 private BluetoothAdapter mAdapter;
The Android Open Source Project33897762009-03-03 19:31:44 -0800311
fredc3c719642012-04-12 00:02:00 -0700312 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
313 new IBluetoothStateChangeCallback.Stub() {
314 public void onBluetoothStateChange(boolean up) {
315 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
316 if (!up) {
Jack He910201b2017-08-22 16:06:54 -0700317 if (VDBG) Log.d(TAG, "Unbinding service...");
Benjamin Franz3362fef2014-11-12 15:57:54 +0000318 doUnbind();
fredc3c719642012-04-12 00:02:00 -0700319 } else {
320 synchronized (mConnection) {
321 try {
322 if (mService == null) {
Jack He910201b2017-08-22 16:06:54 -0700323 if (VDBG) Log.d(TAG, "Binding service...");
Dianne Hackborn3875ec62013-08-04 16:50:16 -0700324 doBind();
fredc3c719642012-04-12 00:02:00 -0700325 }
326 } catch (Exception re) {
Jack He910201b2017-08-22 16:06:54 -0700327 Log.e(TAG, "", re);
fredc3c719642012-04-12 00:02:00 -0700328 }
329 }
330 }
331 }
Jack He910201b2017-08-22 16:06:54 -0700332 };
fredc3c719642012-04-12 00:02:00 -0700333
The Android Open Source Project33897762009-03-03 19:31:44 -0800334 /**
335 * Create a BluetoothHeadset proxy object.
336 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700337 /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800338 mContext = context;
339 mServiceListener = l;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700340 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc3c719642012-04-12 00:02:00 -0700341
342 IBluetoothManager mgr = mAdapter.getBluetoothManager();
343 if (mgr != null) {
344 try {
345 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
346 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700347 Log.e(TAG, "", e);
fredc3c719642012-04-12 00:02:00 -0700348 }
349 }
350
Dianne Hackborn3875ec62013-08-04 16:50:16 -0700351 doBind();
352 }
353
354 boolean doBind() {
Benjamin Franz3362fef2014-11-12 15:57:54 +0000355 try {
356 return mAdapter.getBluetoothManager().bindBluetoothProfileService(
357 BluetoothProfile.HEADSET, mConnection);
358 } catch (RemoteException e) {
359 Log.e(TAG, "Unable to bind HeadsetService", e);
The Android Open Source Project33897762009-03-03 19:31:44 -0800360 }
Benjamin Franz3362fef2014-11-12 15:57:54 +0000361 return false;
362 }
363
364 void doUnbind() {
365 synchronized (mConnection) {
366 if (mService != null) {
367 try {
368 mAdapter.getBluetoothManager().unbindBluetoothProfileService(
369 BluetoothProfile.HEADSET, mConnection);
370 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700371 Log.e(TAG, "Unable to unbind HeadsetService", e);
Benjamin Franz3362fef2014-11-12 15:57:54 +0000372 }
373 }
374 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800375 }
376
The Android Open Source Project33897762009-03-03 19:31:44 -0800377 /**
378 * Close the connection to the backing service.
379 * Other public functions of BluetoothHeadset will return default error
380 * results once close() has been called. Multiple invocations of close()
381 * are ok.
382 */
Matthew Xie78912492012-03-22 17:18:37 -0700383 /*package*/ void close() {
Matthew Xief8035a72012-10-09 22:10:37 -0700384 if (VDBG) log("close()");
fredc3c719642012-04-12 00:02:00 -0700385
386 IBluetoothManager mgr = mAdapter.getBluetoothManager();
387 if (mgr != null) {
388 try {
389 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
390 } catch (Exception e) {
Jack He910201b2017-08-22 16:06:54 -0700391 Log.e(TAG, "", e);
fredc3c719642012-04-12 00:02:00 -0700392 }
393 }
Benjamin Franz733656c2014-12-16 15:33:03 +0000394 mServiceListener = null;
Benjamin Franz3362fef2014-11-12 15:57:54 +0000395 doUnbind();
The Android Open Source Project33897762009-03-03 19:31:44 -0800396 }
397
398 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700399 * Initiate connection to a profile of the remote bluetooth device.
400 *
401 * <p> Currently, the system supports only 1 connection to the
402 * headset/handsfree profile. The API will automatically disconnect connected
403 * devices before connecting.
404 *
405 * <p> This API returns false in scenarios like the profile on the
406 * device is already connected or Bluetooth is not turned on.
407 * When this API returns true, it is guaranteed that
408 * connection state intent for the profile will be broadcasted with
409 * the state. Users can get the connection state of the profile
410 * from this intent.
411 *
412 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
413 * permission.
414 *
415 * @param device Remote Bluetooth Device
Jack He910201b2017-08-22 16:06:54 -0700416 * @return false on immediate error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700417 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800418 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700419 public boolean connect(BluetoothDevice device) {
420 if (DBG) log("connect(" + device + ")");
421 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700422 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800423 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700424 return mService.connect(device);
425 } catch (RemoteException e) {
426 Log.e(TAG, Log.getStackTraceString(new Throwable()));
427 return false;
428 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800429 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700430 if (mService == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800431 return false;
432 }
433
434 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700435 * Initiate disconnection from a profile
436 *
437 * <p> This API will return false in scenarios like the profile on the
438 * Bluetooth device is not in connected state etc. When this API returns,
439 * true, it is guaranteed that the connection state change
440 * intent will be broadcasted with the state. Users can get the
441 * disconnection state of the profile from this intent.
442 *
443 * <p> If the disconnection is initiated by a remote device, the state
444 * will transition from {@link #STATE_CONNECTED} to
445 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
446 * host (local) device the state will transition from
447 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
448 * state {@link #STATE_DISCONNECTED}. The transition to
449 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
450 * two scenarios.
451 *
452 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
453 * permission.
454 *
455 * @param device Remote Bluetooth Device
Jack He910201b2017-08-22 16:06:54 -0700456 * @return false on immediate error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700457 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800458 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700459 public boolean disconnect(BluetoothDevice device) {
460 if (DBG) log("disconnect(" + device + ")");
461 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700462 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800463 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700464 return mService.disconnect(device);
465 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700466 Log.e(TAG, Log.getStackTraceString(new Throwable()));
467 return false;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700468 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800469 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700470 if (mService == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800471 return false;
472 }
473
474 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700475 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800476 */
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700477 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xief8035a72012-10-09 22:10:37 -0700478 if (VDBG) log("getConnectedDevices()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700479 if (mService != null && isEnabled()) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800480 try {
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700481 return mService.getConnectedDevices();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700482 } catch (RemoteException e) {
483 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700484 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700485 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800486 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700487 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700488 return new ArrayList<BluetoothDevice>();
The Android Open Source Project33897762009-03-03 19:31:44 -0800489 }
490
491 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700492 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800493 */
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700494 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xief8035a72012-10-09 22:10:37 -0700495 if (VDBG) log("getDevicesMatchingStates()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700496 if (mService != null && isEnabled()) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800497 try {
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700498 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700499 } catch (RemoteException e) {
500 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700501 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700502 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800503 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700504 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700505 return new ArrayList<BluetoothDevice>();
The Android Open Source Project33897762009-03-03 19:31:44 -0800506 }
507
508 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700509 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800510 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700511 public int getConnectionState(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700512 if (VDBG) log("getConnectionState(" + device + ")");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700513 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700514 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800515 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700516 return mService.getConnectionState(device);
517 } catch (RemoteException e) {
518 Log.e(TAG, Log.getStackTraceString(new Throwable()));
519 return BluetoothProfile.STATE_DISCONNECTED;
520 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800521 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700522 if (mService == null) Log.w(TAG, "Proxy not attached to service");
523 return BluetoothProfile.STATE_DISCONNECTED;
The Android Open Source Project33897762009-03-03 19:31:44 -0800524 }
525
526 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700527 * Set priority of the profile
528 *
529 * <p> The device should already be paired.
Jack He910201b2017-08-22 16:06:54 -0700530 * Priority can be one of {@link #PRIORITY_ON} or
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700531 * {@link #PRIORITY_OFF},
532 *
533 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
534 * permission.
535 *
536 * @param device Paired bluetooth device
537 * @param priority
538 * @return true if priority is set, false on error
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700539 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800540 */
Nick Pelly2d664882009-08-14 18:33:38 -0700541 public boolean setPriority(BluetoothDevice device, int priority) {
542 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700543 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700544 isValidDevice(device)) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700545 if (priority != BluetoothProfile.PRIORITY_OFF &&
Jack He910201b2017-08-22 16:06:54 -0700546 priority != BluetoothProfile.PRIORITY_ON) {
547 return false;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700548 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800549 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700550 return mService.setPriority(device, priority);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700551 } catch (RemoteException e) {
552 Log.e(TAG, Log.getStackTraceString(new Throwable()));
553 return false;
554 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800555 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700556 if (mService == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800557 return false;
558 }
559
560 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700561 * Get the priority of the profile.
562 *
563 * <p> The priority can be any of:
564 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
565 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
566 *
567 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
568 *
569 * @param device Bluetooth device
570 * @return priority of the device
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700571 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800572 */
Nick Pelly2d664882009-08-14 18:33:38 -0700573 public int getPriority(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700574 if (VDBG) log("getPriority(" + device + ")");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700575 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700576 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800577 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700578 return mService.getPriority(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700579 } catch (RemoteException e) {
580 Log.e(TAG, Log.getStackTraceString(new Throwable()));
581 return PRIORITY_OFF;
582 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800583 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700584 if (mService == null) Log.w(TAG, "Proxy not attached to service");
585 return PRIORITY_OFF;
586 }
587
588 /**
589 * Start Bluetooth voice recognition. This methods sends the voice
590 * recognition AT command to the headset and establishes the
591 * audio connection.
592 *
593 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800594 * If this function returns true, this intent will be broadcasted with
595 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700596 *
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800597 * <p> {@link #EXTRA_STATE} will transition from
598 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
599 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
600 * in case of failure to establish the audio connection.
601 *
602 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700603 *
604 * @param device Bluetooth headset
Jack He910201b2017-08-22 16:06:54 -0700605 * @return false if there is no headset connected of if the connected headset doesn't support
606 * voice recognition or on error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700607 */
608 public boolean startVoiceRecognition(BluetoothDevice device) {
609 if (DBG) log("startVoiceRecognition()");
610 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700611 isValidDevice(device)) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700612 try {
613 return mService.startVoiceRecognition(device);
614 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700615 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700616 }
617 }
618 if (mService == null) Log.w(TAG, "Proxy not attached to service");
619 return false;
620 }
621
622 /**
623 * Stop Bluetooth Voice Recognition mode, and shut down the
624 * Bluetooth audio path.
625 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800626 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700627 *
628 * @param device Bluetooth headset
Jack He910201b2017-08-22 16:06:54 -0700629 * @return false if there is no headset connected or on error, true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700630 */
631 public boolean stopVoiceRecognition(BluetoothDevice device) {
632 if (DBG) log("stopVoiceRecognition()");
633 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700634 isValidDevice(device)) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700635 try {
636 return mService.stopVoiceRecognition(device);
637 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700638 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700639 }
640 }
641 if (mService == null) Log.w(TAG, "Proxy not attached to service");
642 return false;
643 }
644
645 /**
646 * Check if Bluetooth SCO audio is connected.
647 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800648 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700649 *
650 * @param device Bluetooth headset
Jack He910201b2017-08-22 16:06:54 -0700651 * @return true if SCO is connected, false otherwise or on error
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700652 */
653 public boolean isAudioConnected(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700654 if (VDBG) log("isAudioConnected()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700655 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700656 isValidDevice(device)) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700657 try {
Jack He910201b2017-08-22 16:06:54 -0700658 return mService.isAudioConnected(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700659 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700660 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700661 }
662 }
663 if (mService == null) Log.w(TAG, "Proxy not attached to service");
664 return false;
The Android Open Source Project33897762009-03-03 19:31:44 -0800665 }
666
667 /**
Nick Pelly312cd3a2009-06-19 10:08:09 -0700668 * Get battery usage hint for Bluetooth Headset service.
669 * This is a monotonically increasing integer. Wraps to 0 at
670 * Integer.MAX_INT, and at boot.
671 * Current implementation returns the number of AT commands handled since
672 * boot. This is a good indicator for spammy headset/handsfree units that
673 * can keep the device awake by polling for cellular status updates. As a
674 * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700675 *
676 * @param device the bluetooth headset.
Jack He910201b2017-08-22 16:06:54 -0700677 * @return monotonically increasing battery usage hint, or a negative error code on error
Nick Pelly312cd3a2009-06-19 10:08:09 -0700678 * @hide
679 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700680 public int getBatteryUsageHint(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700681 if (VDBG) log("getBatteryUsageHint()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700682 if (mService != null && isEnabled() &&
Jack He910201b2017-08-22 16:06:54 -0700683 isValidDevice(device)) {
Nick Pelly312cd3a2009-06-19 10:08:09 -0700684 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700685 return mService.getBatteryUsageHint(device);
686 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700687 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700688 }
Nick Pelly312cd3a2009-06-19 10:08:09 -0700689 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700690 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Nick Pelly312cd3a2009-06-19 10:08:09 -0700691 return -1;
692 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700693
Eric Laurent2e66fa22010-03-17 14:59:27 -0700694 /**
695 * Indicates if current platform supports voice dialing over bluetooth SCO.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700696 *
Eric Laurent2e66fa22010-03-17 14:59:27 -0700697 * @return true if voice dialing over bluetooth is supported, false otherwise.
698 * @hide
699 */
700 public static boolean isBluetoothVoiceDialingEnabled(Context context) {
701 return context.getResources().getBoolean(
702 com.android.internal.R.bool.config_bluetooth_sco_off_call);
703 }
704
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700705 /**
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700706 * Accept the incoming connection.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700707 * Note: This is an internal function and shouldn't be exposed
708 *
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700709 * @hide
710 */
711 public boolean acceptIncomingConnect(BluetoothDevice device) {
712 if (DBG) log("acceptIncomingConnect");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700713 if (mService != null && isEnabled()) {
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700714 try {
715 return mService.acceptIncomingConnect(device);
Jack He910201b2017-08-22 16:06:54 -0700716 } catch (RemoteException e) {
717 Log.e(TAG, e.toString());
718 }
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700719 } else {
720 Log.w(TAG, "Proxy not attached to service");
721 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
722 }
723 return false;
724 }
725
726 /**
Matthew Xie75cf0b92011-06-25 21:47:07 -0700727 * Reject the incoming connection.
Jack He910201b2017-08-22 16:06:54 -0700728 *
Matthew Xie75cf0b92011-06-25 21:47:07 -0700729 * @hide
730 */
731 public boolean rejectIncomingConnect(BluetoothDevice device) {
732 if (DBG) log("rejectIncomingConnect");
733 if (mService != null) {
734 try {
735 return mService.rejectIncomingConnect(device);
Jack He910201b2017-08-22 16:06:54 -0700736 } catch (RemoteException e) {
737 Log.e(TAG, e.toString());
738 }
Matthew Xie75cf0b92011-06-25 21:47:07 -0700739 } else {
740 Log.w(TAG, "Proxy not attached to service");
741 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
742 }
743 return false;
744 }
745
746 /**
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700747 * Get the current audio state of the Headset.
748 * Note: This is an internal function and shouldn't be exposed
749 *
750 * @hide
751 */
752 public int getAudioState(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700753 if (VDBG) log("getAudioState");
Jaikumar Ganesh2fbe8f42011-04-06 11:09:30 -0700754 if (mService != null && !isDisabled()) {
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700755 try {
756 return mService.getAudioState(device);
Jack He910201b2017-08-22 16:06:54 -0700757 } catch (RemoteException e) {
758 Log.e(TAG, e.toString());
759 }
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700760 } else {
761 Log.w(TAG, "Proxy not attached to service");
762 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
763 }
764 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
765 }
766
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700767 /**
Bryce Lee0e154a32015-11-16 08:55:52 -0800768 * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
769 * audio to the HF unless explicitly told to.
770 * This method should be used in cases where the SCO channel is shared between multiple profiles
771 * and must be delegated by a source knowledgeable
772 * Note: This is an internal function and shouldn't be exposed
773 *
774 * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
Bryce Lee0e154a32015-11-16 08:55:52 -0800775 * @hide
776 */
777 public void setAudioRouteAllowed(boolean allowed) {
778 if (VDBG) log("setAudioRouteAllowed");
779 if (mService != null && isEnabled()) {
780 try {
781 mService.setAudioRouteAllowed(allowed);
Jack He910201b2017-08-22 16:06:54 -0700782 } catch (RemoteException e) {
783 Log.e(TAG, e.toString());
784 }
Bryce Lee0e154a32015-11-16 08:55:52 -0800785 } else {
786 Log.w(TAG, "Proxy not attached to service");
787 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
788 }
789 }
790
791 /**
792 * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
793 * Note: This is an internal function and shouldn't be exposed
794 *
795 * @hide
796 */
797 public boolean getAudioRouteAllowed() {
798 if (VDBG) log("getAudioRouteAllowed");
799 if (mService != null && isEnabled()) {
800 try {
801 return mService.getAudioRouteAllowed();
Jack He910201b2017-08-22 16:06:54 -0700802 } catch (RemoteException e) {
803 Log.e(TAG, e.toString());
804 }
Bryce Lee0e154a32015-11-16 08:55:52 -0800805 } else {
806 Log.w(TAG, "Proxy not attached to service");
807 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
808 }
809 return false;
810 }
811
812 /**
Jack He798d7282017-05-09 17:16:01 -0700813 * Force SCO audio to be opened regardless any other restrictions
814 *
Jack He910201b2017-08-22 16:06:54 -0700815 * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio
816 * False to use SCO audio in normal manner
Jack He798d7282017-05-09 17:16:01 -0700817 * @hide
818 */
819 public void setForceScoAudio(boolean forced) {
820 if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
821 if (mService != null && isEnabled()) {
822 try {
823 mService.setForceScoAudio(forced);
824 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700825 Log.e(TAG, e.toString());
Jack He798d7282017-05-09 17:16:01 -0700826 }
827 } else {
828 Log.w(TAG, "Proxy not attached to service");
829 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
830 }
831 }
832
833 /**
Matthew Xief3ee3512012-02-16 16:57:18 -0800834 * Check if Bluetooth SCO audio is connected.
835 *
836 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
837 *
Jack He910201b2017-08-22 16:06:54 -0700838 * @return true if SCO is connected, false otherwise or on error
Matthew Xief3ee3512012-02-16 16:57:18 -0800839 * @hide
840 */
841 public boolean isAudioOn() {
Matthew Xief8035a72012-10-09 22:10:37 -0700842 if (VDBG) log("isAudioOn()");
Matthew Xief3ee3512012-02-16 16:57:18 -0800843 if (mService != null && isEnabled()) {
844 try {
Jack He910201b2017-08-22 16:06:54 -0700845 return mService.isAudioOn();
Matthew Xief3ee3512012-02-16 16:57:18 -0800846 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700847 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Matthew Xief3ee3512012-02-16 16:57:18 -0800848 }
849 }
850 if (mService == null) Log.w(TAG, "Proxy not attached to service");
851 return false;
852
853 }
854
855 /**
856 * Initiates a connection of headset audio.
857 * It setup SCO channel with remote connected headset device.
858 *
Jack He910201b2017-08-22 16:06:54 -0700859 * @return true if successful false if there was some error such as there is no connected
860 * headset
Matthew Xief3ee3512012-02-16 16:57:18 -0800861 * @hide
862 */
863 public boolean connectAudio() {
864 if (mService != null && isEnabled()) {
865 try {
866 return mService.connectAudio();
867 } catch (RemoteException e) {
868 Log.e(TAG, e.toString());
869 }
870 } else {
871 Log.w(TAG, "Proxy not attached to service");
872 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
873 }
874 return false;
875 }
876
877 /**
878 * Initiates a disconnection of headset audio.
879 * It tears down the SCO channel from remote headset device.
880 *
Jack He910201b2017-08-22 16:06:54 -0700881 * @return true if successful false if there was some error such as there is no connected SCO
882 * channel
Matthew Xief3ee3512012-02-16 16:57:18 -0800883 * @hide
884 */
885 public boolean disconnectAudio() {
886 if (mService != null && isEnabled()) {
887 try {
888 return mService.disconnectAudio();
889 } catch (RemoteException e) {
890 Log.e(TAG, e.toString());
891 }
892 } else {
893 Log.w(TAG, "Proxy not attached to service");
894 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
895 }
896 return false;
897 }
898
899 /**
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800900 * Initiates a SCO channel connection with the headset (if connected).
901 * Also initiates a virtual voice call for Handsfree devices as many devices
902 * do not accept SCO audio without a call.
903 * This API allows the handsfree device to be used for routing non-cellular
904 * call audio.
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700905 *
906 * @param device Remote Bluetooth Device
907 * @return true if successful, false if there was some error.
908 * @hide
909 */
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800910 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
911 if (DBG) log("startScoUsingVirtualVoiceCall()");
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700912 if (mService != null && isEnabled() && isValidDevice(device)) {
913 try {
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800914 return mService.startScoUsingVirtualVoiceCall(device);
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700915 } catch (RemoteException e) {
916 Log.e(TAG, e.toString());
917 }
918 } else {
919 Log.w(TAG, "Proxy not attached to service");
920 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
921 }
922 return false;
923 }
924
925 /**
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800926 * Terminates an ongoing SCO connection and the associated virtual
927 * call.
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700928 *
929 * @param device Remote Bluetooth Device
930 * @return true if successful, false if there was some error.
931 * @hide
932 */
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800933 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
934 if (DBG) log("stopScoUsingVirtualVoiceCall()");
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700935 if (mService != null && isEnabled() && isValidDevice(device)) {
936 try {
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800937 return mService.stopScoUsingVirtualVoiceCall(device);
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700938 } catch (RemoteException e) {
939 Log.e(TAG, e.toString());
940 }
941 } else {
942 Log.w(TAG, "Proxy not attached to service");
943 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
944 }
945 return false;
946 }
947
Matthew Xief3ee3512012-02-16 16:57:18 -0800948 /**
949 * Notify Headset of phone state change.
950 * This is a backdoor for phone app to call BluetoothHeadset since
951 * there is currently not a good way to get precise call state change outside
952 * of phone app.
953 *
954 * @hide
955 */
956 public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
Jack He910201b2017-08-22 16:06:54 -0700957 int type) {
Matthew Xief3ee3512012-02-16 16:57:18 -0800958 if (mService != null && isEnabled()) {
959 try {
960 mService.phoneStateChanged(numActive, numHeld, callState, number, type);
961 } catch (RemoteException e) {
962 Log.e(TAG, e.toString());
963 }
964 } else {
965 Log.w(TAG, "Proxy not attached to service");
966 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
967 }
968 }
969
970 /**
Matthew Xief3ee3512012-02-16 16:57:18 -0800971 * Send Headset of CLCC response
972 *
973 * @hide
974 */
975 public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
Jack He910201b2017-08-22 16:06:54 -0700976 String number, int type) {
Matthew Xief3ee3512012-02-16 16:57:18 -0800977 if (mService != null && isEnabled()) {
978 try {
979 mService.clccResponse(index, direction, status, mode, mpty, number, type);
980 } catch (RemoteException e) {
981 Log.e(TAG, e.toString());
982 }
983 } else {
984 Log.w(TAG, "Proxy not attached to service");
985 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
986 }
987 }
988
Edward Jee7c81f1f2013-08-16 04:07:49 -0700989 /**
990 * Sends a vendor-specific unsolicited result code to the headset.
991 *
Jack He910201b2017-08-22 16:06:54 -0700992 * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code
993 * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the
994 * string <code>"+ANDROID: 0"</code> will be sent.
Edward Jee7c81f1f2013-08-16 04:07:49 -0700995 *
Ying Wang29d93892013-08-26 17:48:22 -0700996 * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
Edward Jee7c81f1f2013-08-16 04:07:49 -0700997 *
998 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
999 *
1000 * @param device Bluetooth headset.
1001 * @param command A vendor-specific command.
1002 * @param arg The argument that will be attached to the command.
1003 * @return {@code false} if there is no headset connected, or if the command is not an allowed
Jack He910201b2017-08-22 16:06:54 -07001004 * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
Edward Jee7c81f1f2013-08-16 04:07:49 -07001005 * @throws IllegalArgumentException if {@code command} is {@code null}.
1006 */
1007 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
1008 String arg) {
1009 if (DBG) {
1010 log("sendVendorSpecificResultCode()");
1011 }
1012 if (command == null) {
1013 throw new IllegalArgumentException("command is null");
1014 }
1015 if (mService != null && isEnabled() &&
1016 isValidDevice(device)) {
1017 try {
1018 return mService.sendVendorSpecificResultCode(device, command, arg);
1019 } catch (RemoteException e) {
1020 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1021 }
1022 }
1023 if (mService == null) {
1024 Log.w(TAG, "Proxy not attached to service");
1025 }
1026 return false;
1027 }
1028
Mudumba Ananth80bf6282014-04-27 13:11:00 -07001029 /**
1030 * enable WBS codec setting.
1031 *
Jack He910201b2017-08-22 16:06:54 -07001032 * @return true if successful false if there was some error such as there is no connected
1033 * headset
Mudumba Ananth80bf6282014-04-27 13:11:00 -07001034 * @hide
1035 */
1036 public boolean enableWBS() {
1037 if (mService != null && isEnabled()) {
1038 try {
1039 return mService.enableWBS();
1040 } catch (RemoteException e) {
1041 Log.e(TAG, e.toString());
1042 }
1043 } else {
1044 Log.w(TAG, "Proxy not attached to service");
1045 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1046 }
1047 return false;
1048 }
1049
1050 /**
1051 * disable WBS codec settting. It set NBS codec.
1052 *
Jack He910201b2017-08-22 16:06:54 -07001053 * @return true if successful false if there was some error such as there is no connected
1054 * headset
Mudumba Ananth80bf6282014-04-27 13:11:00 -07001055 * @hide
1056 */
1057 public boolean disableWBS() {
1058 if (mService != null && isEnabled()) {
1059 try {
1060 return mService.disableWBS();
1061 } catch (RemoteException e) {
1062 Log.e(TAG, e.toString());
1063 }
1064 } else {
1065 Log.w(TAG, "Proxy not attached to service");
1066 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1067 }
1068 return false;
1069 }
1070
Mudumba Ananth3246de62016-02-29 02:14:36 -08001071 /**
Jack Hed8d204d2016-11-17 16:19:43 -08001072 * check if in-band ringing is supported for this platform.
1073 *
Jack He910201b2017-08-22 16:06:54 -07001074 * @return true if in-band ringing is supported false if in-band ringing is not supported
Jack Hed8d204d2016-11-17 16:19:43 -08001075 * @hide
1076 */
1077 public static boolean isInbandRingingSupported(Context context) {
1078 return context.getResources().getBoolean(
1079 com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
1080 }
1081
1082 /**
Mudumba Ananth3246de62016-02-29 02:14:36 -08001083 * Send Headset the BIND response from AG to report change in the status of the
1084 * HF indicators to the headset
1085 *
1086 * @param ind_id Assigned Number of the indicator (defined by SIG)
Jack He910201b2017-08-22 16:06:54 -07001087 * @param ind_status possible values- false-Indicator is disabled, no value changes shall be
1088 * sent for this indicator true-Indicator is enabled, value changes may be sent for this
1089 * indicator
Mudumba Ananth3246de62016-02-29 02:14:36 -08001090 * @hide
1091 */
1092 public void bindResponse(int ind_id, boolean ind_status) {
1093 if (mService != null && isEnabled()) {
1094 try {
1095 mService.bindResponse(ind_id, ind_status);
1096 } catch (RemoteException e) {
1097 Log.e(TAG, e.toString());
1098 }
1099 } else {
1100 Log.w(TAG, "Proxy not attached to service");
1101 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1102 }
1103 }
1104
Benjamin Franz3362fef2014-11-12 15:57:54 +00001105 private final IBluetoothProfileServiceConnection mConnection
Jack He910201b2017-08-22 16:06:54 -07001106 = new IBluetoothProfileServiceConnection.Stub() {
Benjamin Franz3362fef2014-11-12 15:57:54 +00001107 @Override
The Android Open Source Project33897762009-03-03 19:31:44 -08001108 public void onServiceConnected(ComponentName className, IBinder service) {
1109 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey73458a82016-11-04 11:23:46 -06001110 mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
Benjamin Franz3362fef2014-11-12 15:57:54 +00001111 mHandler.sendMessage(mHandler.obtainMessage(
1112 MESSAGE_HEADSET_SERVICE_CONNECTED));
The Android Open Source Project33897762009-03-03 19:31:44 -08001113 }
Jack He910201b2017-08-22 16:06:54 -07001114
Benjamin Franz3362fef2014-11-12 15:57:54 +00001115 @Override
The Android Open Source Project33897762009-03-03 19:31:44 -08001116 public void onServiceDisconnected(ComponentName className) {
1117 if (DBG) Log.d(TAG, "Proxy object disconnected");
1118 mService = null;
Benjamin Franz3362fef2014-11-12 15:57:54 +00001119 mHandler.sendMessage(mHandler.obtainMessage(
1120 MESSAGE_HEADSET_SERVICE_DISCONNECTED));
The Android Open Source Project33897762009-03-03 19:31:44 -08001121 }
1122 };
The Android Open Source Project0047a0f2009-03-05 20:00:43 -08001123
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001124 private boolean isEnabled() {
Jack He910201b2017-08-22 16:06:54 -07001125 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
1126 return false;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001127 }
1128
Jaikumar Ganesh2fbe8f42011-04-06 11:09:30 -07001129 private boolean isDisabled() {
Jack He910201b2017-08-22 16:06:54 -07001130 if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
1131 return false;
Jaikumar Ganesh2fbe8f42011-04-06 11:09:30 -07001132 }
1133
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001134 private boolean isValidDevice(BluetoothDevice device) {
Jack He910201b2017-08-22 16:06:54 -07001135 if (device == null) return false;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001136
Jack He910201b2017-08-22 16:06:54 -07001137 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
1138 return false;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001139 }
1140
The Android Open Source Project0047a0f2009-03-05 20:00:43 -08001141 private static void log(String msg) {
1142 Log.d(TAG, msg);
1143 }
Benjamin Franz3362fef2014-11-12 15:57:54 +00001144
1145 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
1146 @Override
1147 public void handleMessage(Message msg) {
1148 switch (msg.what) {
1149 case MESSAGE_HEADSET_SERVICE_CONNECTED: {
1150 if (mServiceListener != null) {
1151 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
1152 BluetoothHeadset.this);
1153 }
1154 break;
1155 }
1156 case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
1157 if (mServiceListener != null) {
1158 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
1159 }
Benjamin Franz3362fef2014-11-12 15:57:54 +00001160 break;
1161 }
1162 }
1163 }
1164 };
The Android Open Source Project33897762009-03-03 19:31:44 -08001165}