blob: c31a9b20e28673c7f5e01236e04b443bab763b64 [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;
Benjamin Franz3362fef2014-11-12 15:57:54 +000023import android.os.Handler;
The Android Open Source Project33897762009-03-03 19:31:44 -080024import android.os.IBinder;
Benjamin Franz3362fef2014-11-12 15:57:54 +000025import android.os.Looper;
26import android.os.Message;
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -070027import android.os.RemoteException;
The Android Open Source Project33897762009-03-03 19:31:44 -080028import android.util.Log;
29
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -070030import java.util.ArrayList;
31import java.util.List;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070032
The Android Open Source Project33897762009-03-03 19:31:44 -080033/**
The Android Open Source Project33897762009-03-03 19:31:44 -080034 * Public API for controlling the Bluetooth Headset Service. This includes both
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070035 * Bluetooth Headset and Handsfree (v1.5) profiles.
The Android Open Source Project33897762009-03-03 19:31:44 -080036 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070037 * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
The Android Open Source Project33897762009-03-03 19:31:44 -080038 * Service via IPC.
39 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070040 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
41 * the BluetoothHeadset proxy object. Use
42 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
The Android Open Source Project33897762009-03-03 19:31:44 -080043 *
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070044 * <p> Android only supports one connected Bluetooth Headset at a time.
45 * Each method is protected with its appropriate permission.
The Android Open Source Project33897762009-03-03 19:31:44 -080046 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070047public final class BluetoothHeadset implements BluetoothProfile {
The Android Open Source Project33897762009-03-03 19:31:44 -080048 private static final String TAG = "BluetoothHeadset";
fredc3c719642012-04-12 00:02:00 -070049 private static final boolean DBG = true;
Matthew Xief8035a72012-10-09 22:10:37 -070050 private static final boolean VDBG = false;
The Android Open Source Project33897762009-03-03 19:31:44 -080051
Nick Pellydac4c0d2009-09-10 10:21:56 -070052 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070053 * Intent used to broadcast the change in connection state of the Headset
54 * profile.
55 *
56 * <p>This intent will have 3 extras:
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080057 * <ul>
58 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
59 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
60 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
61 * </ul>
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -080062 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070063 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
64 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
65 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080066 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
67 * receive.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070068 */
69 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
70 public static final String ACTION_CONNECTION_STATE_CHANGED =
71 "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
72
73 /**
74 * Intent used to broadcast the change in the Audio Connection state of the
75 * A2DP profile.
76 *
77 * <p>This intent will have 3 extras:
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080078 * <ul>
79 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
80 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
81 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
82 * </ul>
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -080083 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070084 * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
85 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080086 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
87 * to receive.
Nick Pellydac4c0d2009-09-10 10:21:56 -070088 */
89 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
90 public static final String ACTION_AUDIO_STATE_CHANGED =
Jaikumar Ganesh2af07762010-08-24 17:36:13 -070091 "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
Nick Pellydac4c0d2009-09-10 10:21:56 -070092
Jaikumar Ganeshf48cda52010-04-02 14:44:43 -070093
Nick Pellydac4c0d2009-09-10 10:21:56 -070094 /**
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -070095 * Intent used to broadcast that the headset has posted a
96 * vendor-specific event.
97 *
98 * <p>This intent will have 4 extras and 1 category.
Jaikumar Ganeshb3427572011-01-25 16:03:13 -080099 * <ul>
100 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
101 * </li>
102 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
103 * specific command </li>
104 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
105 * command type which can be one of {@link #AT_CMD_TYPE_READ},
106 * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -0800107 * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800108 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
109 * arguments. </li>
110 * </ul>
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700111 *
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -0800112 *<p> The category is the Company ID of the vendor defining the
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700113 * vendor-specific command. {@link BluetoothAssignedNumbers}
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700114 *
115 * For example, for Plantronics specific events
116 * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
117 *
118 * <p> For example, an AT+XEVENT=foo,3 will get translated into
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800119 * <ul>
120 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
121 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
Jaikumar Ganeshaa0427e2011-01-26 11:46:56 -0800122 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800123 * </ul>
124 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
125 * to receive.
Herb Jellinekaad41c52010-08-10 13:17:43 -0700126 */
127 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
128 public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
129 "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
130
131 /**
132 * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
133 * intents that contains the name of the vendor-specific command.
134 */
135 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
136 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
137
138 /**
139 * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700140 * intents that contains the AT command type of the vendor-specific command.
Herb Jellinekaad41c52010-08-10 13:17:43 -0700141 */
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700142 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
143 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
144
145 /**
146 * AT command type READ used with
147 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
148 * For example, AT+VGM?. There are no arguments for this command type.
149 */
150 public static final int AT_CMD_TYPE_READ = 0;
151
152 /**
153 * AT command type TEST used with
154 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
155 * For example, AT+VGM=?. There are no arguments for this command type.
156 */
157 public static final int AT_CMD_TYPE_TEST = 1;
158
159 /**
160 * AT command type SET used with
161 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
162 * For example, AT+VGM=<args>.
163 */
164 public static final int AT_CMD_TYPE_SET = 2;
165
166 /**
167 * AT command type BASIC used with
168 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
169 * For example, ATD. Single character commands and everything following the
170 * character are arguments.
171 */
172 public static final int AT_CMD_TYPE_BASIC = 3;
173
174 /**
175 * AT command type ACTION used with
176 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
177 * For example, AT+CHUP. There are no arguments for action commands.
178 */
179 public static final int AT_CMD_TYPE_ACTION = 4;
Herb Jellinekaad41c52010-08-10 13:17:43 -0700180
181 /**
182 * A Parcelable String array extra field in
183 * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
184 * the arguments to the vendor-specific command.
185 */
186 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
187 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
188
Jaikumar Ganeshee628ed2010-09-29 11:34:59 -0700189 /**
190 * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
191 * for the companyId
192 */
193 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
194 "android.bluetooth.headset.intent.category.companyid";
195
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700196 /**
Edward Jee7c81f1f2013-08-16 04:07:49 -0700197 * A vendor-specific command for unsolicited result code.
198 */
199 public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
200
201 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800202 * Headset state when SCO audio is not connected.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700203 * This state can be one of
204 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
205 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
206 */
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800207 public static final int STATE_AUDIO_DISCONNECTED = 10;
Herb Jellinekaad41c52010-08-10 13:17:43 -0700208
209 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800210 * Headset state when SCO audio is connecting.
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700211 * This state can be one of
212 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
213 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700214 */
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800215 public static final int STATE_AUDIO_CONNECTING = 11;
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700216
217 /**
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800218 * Headset state when SCO audio is connected.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700219 * This state can be one of
220 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
221 * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
Nick Pellydac4c0d2009-09-10 10:21:56 -0700222 */
Mudumba Ananth3246de62016-02-29 02:14:36 -0800223
224 /**
225 * Intent used to broadcast the headset's indicator status
226 *
227 * <p>This intent will have 3 extras:
228 * <ul>
229 * <li> {@link #EXTRA_IND_ID} - The Assigned number of headset Indicator which is supported by
230 the headset ( as indicated by AT+BIND
231 command in the SLC sequence).or whose value
232 is changed (indicated by AT+BIEV command)</li>
233 * <li> {@link #EXTRA_IND_VALUE}- The updated value of headset indicator. </li>
234 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
235 * </ul>
236 * <p>{@link #EXTRA_IND_ID} is defined by Bluetooth SIG and each of the indicators are
237 * given an assigned number. Below shows the assigned number of Indicator added so far
238 * - Enhanced Safety - 1
239 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
240 * receive.
241 * @hide
242 */
243 public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
244 "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
245
246 /**
247 * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
248 * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG)
249 * that is being sent.
250 * @hide
251 */
252 public static final String EXTRA_HF_INDICATORS_IND_ID =
253 "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
254
255 /**
256 * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
257 * intents that contains the value of the Headset indicator that is being sent.
258 * @hide
259 */
260 public static final String EXTRA_HF_INDICATORS_IND_VALUE =
261 "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
262
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800263 public static final int STATE_AUDIO_CONNECTED = 12;
264
Benjamin Franz3362fef2014-11-12 15:57:54 +0000265 private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
266 private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700267
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700268 private Context mContext;
269 private ServiceListener mServiceListener;
The Android Open Source Project33897762009-03-03 19:31:44 -0800270 private IBluetoothHeadset mService;
Matthew Xie5e9fb032012-03-21 23:15:06 -0700271 private BluetoothAdapter mAdapter;
The Android Open Source Project33897762009-03-03 19:31:44 -0800272
fredc3c719642012-04-12 00:02:00 -0700273 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
274 new IBluetoothStateChangeCallback.Stub() {
275 public void onBluetoothStateChange(boolean up) {
276 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
277 if (!up) {
Matthew Xief8035a72012-10-09 22:10:37 -0700278 if (VDBG) Log.d(TAG,"Unbinding service...");
Benjamin Franz3362fef2014-11-12 15:57:54 +0000279 doUnbind();
fredc3c719642012-04-12 00:02:00 -0700280 } else {
281 synchronized (mConnection) {
282 try {
283 if (mService == null) {
Matthew Xief8035a72012-10-09 22:10:37 -0700284 if (VDBG) Log.d(TAG,"Binding service...");
Dianne Hackborn3875ec62013-08-04 16:50:16 -0700285 doBind();
fredc3c719642012-04-12 00:02:00 -0700286 }
287 } catch (Exception re) {
288 Log.e(TAG,"",re);
289 }
290 }
291 }
292 }
293 };
294
The Android Open Source Project33897762009-03-03 19:31:44 -0800295 /**
296 * Create a BluetoothHeadset proxy object.
297 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700298 /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800299 mContext = context;
300 mServiceListener = l;
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700301 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc3c719642012-04-12 00:02:00 -0700302
303 IBluetoothManager mgr = mAdapter.getBluetoothManager();
304 if (mgr != null) {
305 try {
306 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
307 } catch (RemoteException e) {
308 Log.e(TAG,"",e);
309 }
310 }
311
Dianne Hackborn3875ec62013-08-04 16:50:16 -0700312 doBind();
313 }
314
315 boolean doBind() {
Benjamin Franz3362fef2014-11-12 15:57:54 +0000316 try {
317 return mAdapter.getBluetoothManager().bindBluetoothProfileService(
318 BluetoothProfile.HEADSET, mConnection);
319 } catch (RemoteException e) {
320 Log.e(TAG, "Unable to bind HeadsetService", e);
The Android Open Source Project33897762009-03-03 19:31:44 -0800321 }
Benjamin Franz3362fef2014-11-12 15:57:54 +0000322 return false;
323 }
324
325 void doUnbind() {
326 synchronized (mConnection) {
327 if (mService != null) {
328 try {
329 mAdapter.getBluetoothManager().unbindBluetoothProfileService(
330 BluetoothProfile.HEADSET, mConnection);
331 } catch (RemoteException e) {
332 Log.e(TAG,"Unable to unbind HeadsetService", e);
333 }
334 }
335 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800336 }
337
The Android Open Source Project33897762009-03-03 19:31:44 -0800338 /**
339 * Close the connection to the backing service.
340 * Other public functions of BluetoothHeadset will return default error
341 * results once close() has been called. Multiple invocations of close()
342 * are ok.
343 */
Matthew Xie78912492012-03-22 17:18:37 -0700344 /*package*/ void close() {
Matthew Xief8035a72012-10-09 22:10:37 -0700345 if (VDBG) log("close()");
fredc3c719642012-04-12 00:02:00 -0700346
347 IBluetoothManager mgr = mAdapter.getBluetoothManager();
348 if (mgr != null) {
349 try {
350 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
351 } catch (Exception e) {
352 Log.e(TAG,"",e);
353 }
354 }
Benjamin Franz733656c2014-12-16 15:33:03 +0000355 mServiceListener = null;
Benjamin Franz3362fef2014-11-12 15:57:54 +0000356 doUnbind();
The Android Open Source Project33897762009-03-03 19:31:44 -0800357 }
358
359 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700360 * Initiate connection to a profile of the remote bluetooth device.
361 *
362 * <p> Currently, the system supports only 1 connection to the
363 * headset/handsfree profile. The API will automatically disconnect connected
364 * devices before connecting.
365 *
366 * <p> This API returns false in scenarios like the profile on the
367 * device is already connected or Bluetooth is not turned on.
368 * When this API returns true, it is guaranteed that
369 * connection state intent for the profile will be broadcasted with
370 * the state. Users can get the connection state of the profile
371 * from this intent.
372 *
373 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
374 * permission.
375 *
376 * @param device Remote Bluetooth Device
377 * @return false on immediate error,
378 * true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700379 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800380 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700381 public boolean connect(BluetoothDevice device) {
382 if (DBG) log("connect(" + device + ")");
383 if (mService != null && isEnabled() &&
384 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800385 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700386 return mService.connect(device);
387 } catch (RemoteException e) {
388 Log.e(TAG, Log.getStackTraceString(new Throwable()));
389 return false;
390 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800391 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700392 if (mService == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800393 return false;
394 }
395
396 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700397 * Initiate disconnection from a profile
398 *
399 * <p> This API will return false in scenarios like the profile on the
400 * Bluetooth device is not in connected state etc. When this API returns,
401 * true, it is guaranteed that the connection state change
402 * intent will be broadcasted with the state. Users can get the
403 * disconnection state of the profile from this intent.
404 *
405 * <p> If the disconnection is initiated by a remote device, the state
406 * will transition from {@link #STATE_CONNECTED} to
407 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
408 * host (local) device the state will transition from
409 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
410 * state {@link #STATE_DISCONNECTED}. The transition to
411 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
412 * two scenarios.
413 *
414 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
415 * permission.
416 *
417 * @param device Remote Bluetooth Device
418 * @return false on immediate error,
419 * true otherwise
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700420 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800421 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700422 public boolean disconnect(BluetoothDevice device) {
423 if (DBG) log("disconnect(" + device + ")");
424 if (mService != null && isEnabled() &&
425 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800426 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700427 return mService.disconnect(device);
428 } catch (RemoteException e) {
429 Log.e(TAG, Log.getStackTraceString(new Throwable()));
430 return false;
431 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800432 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700433 if (mService == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800434 return false;
435 }
436
437 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700438 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800439 */
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700440 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xief8035a72012-10-09 22:10:37 -0700441 if (VDBG) log("getConnectedDevices()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700442 if (mService != null && isEnabled()) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800443 try {
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700444 return mService.getConnectedDevices();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700445 } catch (RemoteException e) {
446 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700447 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700448 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800449 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700450 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700451 return new ArrayList<BluetoothDevice>();
The Android Open Source Project33897762009-03-03 19:31:44 -0800452 }
453
454 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700455 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800456 */
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700457 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xief8035a72012-10-09 22:10:37 -0700458 if (VDBG) log("getDevicesMatchingStates()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700459 if (mService != null && isEnabled()) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800460 try {
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700461 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700462 } catch (RemoteException e) {
463 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700464 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700465 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800466 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700467 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganeshd8fc4dd2010-10-18 16:41:53 -0700468 return new ArrayList<BluetoothDevice>();
The Android Open Source Project33897762009-03-03 19:31:44 -0800469 }
470
471 /**
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700472 * {@inheritDoc}
The Android Open Source Project33897762009-03-03 19:31:44 -0800473 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700474 public int getConnectionState(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700475 if (VDBG) log("getConnectionState(" + device + ")");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700476 if (mService != null && isEnabled() &&
477 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800478 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700479 return mService.getConnectionState(device);
480 } catch (RemoteException e) {
481 Log.e(TAG, Log.getStackTraceString(new Throwable()));
482 return BluetoothProfile.STATE_DISCONNECTED;
483 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800484 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700485 if (mService == null) Log.w(TAG, "Proxy not attached to service");
486 return BluetoothProfile.STATE_DISCONNECTED;
The Android Open Source Project33897762009-03-03 19:31:44 -0800487 }
488
489 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700490 * Set priority of the profile
491 *
492 * <p> The device should already be paired.
493 * Priority can be one of {@link #PRIORITY_ON} or
494 * {@link #PRIORITY_OFF},
495 *
496 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
497 * permission.
498 *
499 * @param device Paired bluetooth device
500 * @param priority
501 * @return true if priority is set, false on error
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700502 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800503 */
Nick Pelly2d664882009-08-14 18:33:38 -0700504 public boolean setPriority(BluetoothDevice device, int priority) {
505 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700506 if (mService != null && isEnabled() &&
507 isValidDevice(device)) {
508 if (priority != BluetoothProfile.PRIORITY_OFF &&
Ganesh Ganapathi Batta20867a72012-07-31 16:08:17 -0700509 priority != BluetoothProfile.PRIORITY_ON) {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700510 return false;
511 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800512 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700513 return mService.setPriority(device, priority);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700514 } catch (RemoteException e) {
515 Log.e(TAG, Log.getStackTraceString(new Throwable()));
516 return false;
517 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800518 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700519 if (mService == null) Log.w(TAG, "Proxy not attached to service");
The Android Open Source Project33897762009-03-03 19:31:44 -0800520 return false;
521 }
522
523 /**
Jaikumar Ganesha7266d32011-05-26 13:56:40 -0700524 * Get the priority of the profile.
525 *
526 * <p> The priority can be any of:
527 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
528 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
529 *
530 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
531 *
532 * @param device Bluetooth device
533 * @return priority of the device
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700534 * @hide
The Android Open Source Project33897762009-03-03 19:31:44 -0800535 */
Nick Pelly2d664882009-08-14 18:33:38 -0700536 public int getPriority(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700537 if (VDBG) log("getPriority(" + device + ")");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700538 if (mService != null && isEnabled() &&
539 isValidDevice(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800540 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700541 return mService.getPriority(device);
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700542 } catch (RemoteException e) {
543 Log.e(TAG, Log.getStackTraceString(new Throwable()));
544 return PRIORITY_OFF;
545 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800546 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700547 if (mService == null) Log.w(TAG, "Proxy not attached to service");
548 return PRIORITY_OFF;
549 }
550
551 /**
552 * Start Bluetooth voice recognition. This methods sends the voice
553 * recognition AT command to the headset and establishes the
554 * audio connection.
555 *
556 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800557 * If this function returns true, this intent will be broadcasted with
558 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700559 *
Jaikumar Ganesh8ea890d2010-11-11 10:49:46 -0800560 * <p> {@link #EXTRA_STATE} will transition from
561 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
562 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
563 * in case of failure to establish the audio connection.
564 *
565 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700566 *
567 * @param device Bluetooth headset
568 * @return false if there is no headset connected of if the
569 * connected headset doesn't support voice recognition
570 * or on error, true otherwise
571 */
572 public boolean startVoiceRecognition(BluetoothDevice device) {
573 if (DBG) log("startVoiceRecognition()");
574 if (mService != null && isEnabled() &&
575 isValidDevice(device)) {
576 try {
577 return mService.startVoiceRecognition(device);
578 } catch (RemoteException e) {
579 Log.e(TAG, Log.getStackTraceString(new Throwable()));
580 }
581 }
582 if (mService == null) Log.w(TAG, "Proxy not attached to service");
583 return false;
584 }
585
586 /**
587 * Stop Bluetooth Voice Recognition mode, and shut down the
588 * Bluetooth audio path.
589 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800590 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700591 *
592 * @param device Bluetooth headset
593 * @return false if there is no headset connected
594 * or on error, true otherwise
595 */
596 public boolean stopVoiceRecognition(BluetoothDevice device) {
597 if (DBG) log("stopVoiceRecognition()");
598 if (mService != null && isEnabled() &&
599 isValidDevice(device)) {
600 try {
601 return mService.stopVoiceRecognition(device);
602 } catch (RemoteException e) {
603 Log.e(TAG, Log.getStackTraceString(new Throwable()));
604 }
605 }
606 if (mService == null) Log.w(TAG, "Proxy not attached to service");
607 return false;
608 }
609
610 /**
611 * Check if Bluetooth SCO audio is connected.
612 *
Jaikumar Ganeshb3427572011-01-25 16:03:13 -0800613 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700614 *
615 * @param device Bluetooth headset
616 * @return true if SCO is connected,
617 * false otherwise or on error
618 */
619 public boolean isAudioConnected(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700620 if (VDBG) log("isAudioConnected()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700621 if (mService != null && isEnabled() &&
622 isValidDevice(device)) {
623 try {
624 return mService.isAudioConnected(device);
625 } catch (RemoteException e) {
626 Log.e(TAG, Log.getStackTraceString(new Throwable()));
627 }
628 }
629 if (mService == null) Log.w(TAG, "Proxy not attached to service");
630 return false;
The Android Open Source Project33897762009-03-03 19:31:44 -0800631 }
632
633 /**
Nick Pelly312cd3a2009-06-19 10:08:09 -0700634 * Get battery usage hint for Bluetooth Headset service.
635 * This is a monotonically increasing integer. Wraps to 0 at
636 * Integer.MAX_INT, and at boot.
637 * Current implementation returns the number of AT commands handled since
638 * boot. This is a good indicator for spammy headset/handsfree units that
639 * can keep the device awake by polling for cellular status updates. As a
640 * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700641 *
642 * @param device the bluetooth headset.
Nick Pelly312cd3a2009-06-19 10:08:09 -0700643 * @return monotonically increasing battery usage hint, or a negative error
644 * code on error
645 * @hide
646 */
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700647 public int getBatteryUsageHint(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700648 if (VDBG) log("getBatteryUsageHint()");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700649 if (mService != null && isEnabled() &&
650 isValidDevice(device)) {
Nick Pelly312cd3a2009-06-19 10:08:09 -0700651 try {
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700652 return mService.getBatteryUsageHint(device);
653 } catch (RemoteException e) {
654 Log.e(TAG, Log.getStackTraceString(new Throwable()));
655 }
Nick Pelly312cd3a2009-06-19 10:08:09 -0700656 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700657 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Nick Pelly312cd3a2009-06-19 10:08:09 -0700658 return -1;
659 }
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700660
Eric Laurent2e66fa22010-03-17 14:59:27 -0700661 /**
662 * Indicates if current platform supports voice dialing over bluetooth SCO.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700663 *
Eric Laurent2e66fa22010-03-17 14:59:27 -0700664 * @return true if voice dialing over bluetooth is supported, false otherwise.
665 * @hide
666 */
667 public static boolean isBluetoothVoiceDialingEnabled(Context context) {
668 return context.getResources().getBoolean(
669 com.android.internal.R.bool.config_bluetooth_sco_off_call);
670 }
671
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700672 /**
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700673 * Accept the incoming connection.
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700674 * Note: This is an internal function and shouldn't be exposed
675 *
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700676 * @hide
677 */
678 public boolean acceptIncomingConnect(BluetoothDevice device) {
679 if (DBG) log("acceptIncomingConnect");
Jaikumar Ganesh2af07762010-08-24 17:36:13 -0700680 if (mService != null && isEnabled()) {
Jaikumar Ganeshb84bbd92010-06-02 14:36:14 -0700681 try {
682 return mService.acceptIncomingConnect(device);
683 } catch (RemoteException e) {Log.e(TAG, e.toString());}
684 } else {
685 Log.w(TAG, "Proxy not attached to service");
686 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
687 }
688 return false;
689 }
690
691 /**
Matthew Xie75cf0b92011-06-25 21:47:07 -0700692 * Reject the incoming connection.
693 * @hide
694 */
695 public boolean rejectIncomingConnect(BluetoothDevice device) {
696 if (DBG) log("rejectIncomingConnect");
697 if (mService != null) {
698 try {
699 return mService.rejectIncomingConnect(device);
700 } catch (RemoteException e) {Log.e(TAG, e.toString());}
701 } else {
702 Log.w(TAG, "Proxy not attached to service");
703 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
704 }
705 return false;
706 }
707
708 /**
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700709 * Get the current audio state of the Headset.
710 * Note: This is an internal function and shouldn't be exposed
711 *
712 * @hide
713 */
714 public int getAudioState(BluetoothDevice device) {
Matthew Xief8035a72012-10-09 22:10:37 -0700715 if (VDBG) log("getAudioState");
Jaikumar Ganesh2fbe8f42011-04-06 11:09:30 -0700716 if (mService != null && !isDisabled()) {
Jaikumar Ganesh23501e22010-11-01 11:59:57 -0700717 try {
718 return mService.getAudioState(device);
719 } catch (RemoteException e) {Log.e(TAG, e.toString());}
720 } else {
721 Log.w(TAG, "Proxy not attached to service");
722 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
723 }
724 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
725 }
726
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700727 /**
Bryce Lee0e154a32015-11-16 08:55:52 -0800728 * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
729 * audio to the HF unless explicitly told to.
730 * This method should be used in cases where the SCO channel is shared between multiple profiles
731 * and must be delegated by a source knowledgeable
732 * Note: This is an internal function and shouldn't be exposed
733 *
734 * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
735 *
736 * @hide
737 */
738 public void setAudioRouteAllowed(boolean allowed) {
739 if (VDBG) log("setAudioRouteAllowed");
740 if (mService != null && isEnabled()) {
741 try {
742 mService.setAudioRouteAllowed(allowed);
743 } catch (RemoteException e) {Log.e(TAG, e.toString());}
744 } else {
745 Log.w(TAG, "Proxy not attached to service");
746 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
747 }
748 }
749
750 /**
751 * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
752 * Note: This is an internal function and shouldn't be exposed
753 *
754 * @hide
755 */
756 public boolean getAudioRouteAllowed() {
757 if (VDBG) log("getAudioRouteAllowed");
758 if (mService != null && isEnabled()) {
759 try {
760 return mService.getAudioRouteAllowed();
761 } catch (RemoteException e) {Log.e(TAG, e.toString());}
762 } else {
763 Log.w(TAG, "Proxy not attached to service");
764 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
765 }
766 return false;
767 }
768
769 /**
Jack He798d7282017-05-09 17:16:01 -0700770 * Force SCO audio to be opened regardless any other restrictions
771 *
772 * @param forced Whether or not SCO audio connection should be forced:
773 * True to force SCO audio
774 * False to use SCO audio in normal manner
775 * @hide
776 */
777 public void setForceScoAudio(boolean forced) {
778 if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
779 if (mService != null && isEnabled()) {
780 try {
781 mService.setForceScoAudio(forced);
782 } catch (RemoteException e) {
783 Log.e(TAG, e.toString());
784 }
785 } else {
786 Log.w(TAG, "Proxy not attached to service");
787 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
788 }
789 }
790
791 /**
Matthew Xief3ee3512012-02-16 16:57:18 -0800792 * Check if Bluetooth SCO audio is connected.
793 *
794 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
795 *
796 * @return true if SCO is connected,
797 * false otherwise or on error
798 * @hide
799 */
800 public boolean isAudioOn() {
Matthew Xief8035a72012-10-09 22:10:37 -0700801 if (VDBG) log("isAudioOn()");
Matthew Xief3ee3512012-02-16 16:57:18 -0800802 if (mService != null && isEnabled()) {
803 try {
804 return mService.isAudioOn();
805 } catch (RemoteException e) {
806 Log.e(TAG, Log.getStackTraceString(new Throwable()));
807 }
808 }
809 if (mService == null) Log.w(TAG, "Proxy not attached to service");
810 return false;
811
812 }
813
814 /**
815 * Initiates a connection of headset audio.
816 * It setup SCO channel with remote connected headset device.
817 *
818 * @return true if successful
819 * false if there was some error such as
820 * there is no connected headset
821 * @hide
822 */
823 public boolean connectAudio() {
824 if (mService != null && isEnabled()) {
825 try {
826 return mService.connectAudio();
827 } catch (RemoteException e) {
828 Log.e(TAG, e.toString());
829 }
830 } else {
831 Log.w(TAG, "Proxy not attached to service");
832 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
833 }
834 return false;
835 }
836
837 /**
838 * Initiates a disconnection of headset audio.
839 * It tears down the SCO channel from remote headset device.
840 *
841 * @return true if successful
842 * false if there was some error such as
843 * there is no connected SCO channel
844 * @hide
845 */
846 public boolean disconnectAudio() {
847 if (mService != null && isEnabled()) {
848 try {
849 return mService.disconnectAudio();
850 } catch (RemoteException e) {
851 Log.e(TAG, e.toString());
852 }
853 } else {
854 Log.w(TAG, "Proxy not attached to service");
855 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
856 }
857 return false;
858 }
859
860 /**
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800861 * Initiates a SCO channel connection with the headset (if connected).
862 * Also initiates a virtual voice call for Handsfree devices as many devices
863 * do not accept SCO audio without a call.
864 * This API allows the handsfree device to be used for routing non-cellular
865 * call audio.
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700866 *
867 * @param device Remote Bluetooth Device
868 * @return true if successful, false if there was some error.
869 * @hide
870 */
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800871 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
872 if (DBG) log("startScoUsingVirtualVoiceCall()");
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700873 if (mService != null && isEnabled() && isValidDevice(device)) {
874 try {
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800875 return mService.startScoUsingVirtualVoiceCall(device);
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700876 } catch (RemoteException e) {
877 Log.e(TAG, e.toString());
878 }
879 } else {
880 Log.w(TAG, "Proxy not attached to service");
881 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
882 }
883 return false;
884 }
885
886 /**
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800887 * Terminates an ongoing SCO connection and the associated virtual
888 * call.
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700889 *
890 * @param device Remote Bluetooth Device
891 * @return true if successful, false if there was some error.
892 * @hide
893 */
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800894 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
895 if (DBG) log("stopScoUsingVirtualVoiceCall()");
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700896 if (mService != null && isEnabled() && isValidDevice(device)) {
897 try {
Jaikumar Ganesheea6d262011-01-24 13:55:27 -0800898 return mService.stopScoUsingVirtualVoiceCall(device);
Jaikumar Ganesh7af32b42010-10-26 17:10:09 -0700899 } catch (RemoteException e) {
900 Log.e(TAG, e.toString());
901 }
902 } else {
903 Log.w(TAG, "Proxy not attached to service");
904 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
905 }
906 return false;
907 }
908
Matthew Xief3ee3512012-02-16 16:57:18 -0800909 /**
910 * Notify Headset of phone state change.
911 * This is a backdoor for phone app to call BluetoothHeadset since
912 * there is currently not a good way to get precise call state change outside
913 * of phone app.
914 *
915 * @hide
916 */
917 public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
918 int type) {
919 if (mService != null && isEnabled()) {
920 try {
921 mService.phoneStateChanged(numActive, numHeld, callState, number, type);
922 } catch (RemoteException e) {
923 Log.e(TAG, e.toString());
924 }
925 } else {
926 Log.w(TAG, "Proxy not attached to service");
927 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
928 }
929 }
930
931 /**
Matthew Xief3ee3512012-02-16 16:57:18 -0800932 * Send Headset of CLCC response
933 *
934 * @hide
935 */
936 public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
937 String number, int type) {
938 if (mService != null && isEnabled()) {
939 try {
940 mService.clccResponse(index, direction, status, mode, mpty, number, type);
941 } catch (RemoteException e) {
942 Log.e(TAG, e.toString());
943 }
944 } else {
945 Log.w(TAG, "Proxy not attached to service");
946 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
947 }
948 }
949
Edward Jee7c81f1f2013-08-16 04:07:49 -0700950 /**
951 * Sends a vendor-specific unsolicited result code to the headset.
952 *
953 * <p>The actual string to be sent is <code>command + ": " + arg</code>.
Ying Wang29d93892013-08-26 17:48:22 -0700954 * For example, if {@code command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg}
Edward Jee7c81f1f2013-08-16 04:07:49 -0700955 * is {@code "0"}, the string <code>"+ANDROID: 0"</code> will be sent.
956 *
Ying Wang29d93892013-08-26 17:48:22 -0700957 * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
Edward Jee7c81f1f2013-08-16 04:07:49 -0700958 *
959 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
960 *
961 * @param device Bluetooth headset.
962 * @param command A vendor-specific command.
963 * @param arg The argument that will be attached to the command.
964 * @return {@code false} if there is no headset connected, or if the command is not an allowed
965 * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
966 * @throws IllegalArgumentException if {@code command} is {@code null}.
967 */
968 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
969 String arg) {
970 if (DBG) {
971 log("sendVendorSpecificResultCode()");
972 }
973 if (command == null) {
974 throw new IllegalArgumentException("command is null");
975 }
976 if (mService != null && isEnabled() &&
977 isValidDevice(device)) {
978 try {
979 return mService.sendVendorSpecificResultCode(device, command, arg);
980 } catch (RemoteException e) {
981 Log.e(TAG, Log.getStackTraceString(new Throwable()));
982 }
983 }
984 if (mService == null) {
985 Log.w(TAG, "Proxy not attached to service");
986 }
987 return false;
988 }
989
Mudumba Ananth80bf6282014-04-27 13:11:00 -0700990 /**
991 * enable WBS codec setting.
992 *
993 * @return true if successful
994 * false if there was some error such as
995 * there is no connected headset
996 * @hide
997 */
998 public boolean enableWBS() {
999 if (mService != null && isEnabled()) {
1000 try {
1001 return mService.enableWBS();
1002 } catch (RemoteException e) {
1003 Log.e(TAG, e.toString());
1004 }
1005 } else {
1006 Log.w(TAG, "Proxy not attached to service");
1007 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1008 }
1009 return false;
1010 }
1011
1012 /**
1013 * disable WBS codec settting. It set NBS codec.
1014 *
1015 * @return true if successful
1016 * false if there was some error such as
1017 * there is no connected headset
1018 * @hide
1019 */
1020 public boolean disableWBS() {
1021 if (mService != null && isEnabled()) {
1022 try {
1023 return mService.disableWBS();
1024 } catch (RemoteException e) {
1025 Log.e(TAG, e.toString());
1026 }
1027 } else {
1028 Log.w(TAG, "Proxy not attached to service");
1029 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1030 }
1031 return false;
1032 }
1033
Mudumba Ananth3246de62016-02-29 02:14:36 -08001034 /**
Jack Hed8d204d2016-11-17 16:19:43 -08001035 * check if in-band ringing is supported for this platform.
1036 *
1037 * @return true if in-band ringing is supported
1038 * false if in-band ringing is not supported
1039 * @hide
1040 */
1041 public static boolean isInbandRingingSupported(Context context) {
1042 return context.getResources().getBoolean(
1043 com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
1044 }
1045
1046 /**
Mudumba Ananth3246de62016-02-29 02:14:36 -08001047 * Send Headset the BIND response from AG to report change in the status of the
1048 * HF indicators to the headset
1049 *
1050 * @param ind_id Assigned Number of the indicator (defined by SIG)
1051 * @param ind_status
1052 * possible values- false-Indicator is disabled, no value changes shall be sent for this indicator
1053 * true-Indicator is enabled, value changes may be sent for this indicator
1054 * @hide
1055 */
1056 public void bindResponse(int ind_id, boolean ind_status) {
1057 if (mService != null && isEnabled()) {
1058 try {
1059 mService.bindResponse(ind_id, ind_status);
1060 } catch (RemoteException e) {
1061 Log.e(TAG, e.toString());
1062 }
1063 } else {
1064 Log.w(TAG, "Proxy not attached to service");
1065 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1066 }
1067 }
1068
Benjamin Franz3362fef2014-11-12 15:57:54 +00001069 private final IBluetoothProfileServiceConnection mConnection
1070 = new IBluetoothProfileServiceConnection.Stub() {
1071 @Override
The Android Open Source Project33897762009-03-03 19:31:44 -08001072 public void onServiceConnected(ComponentName className, IBinder service) {
1073 if (DBG) Log.d(TAG, "Proxy object connected");
1074 mService = IBluetoothHeadset.Stub.asInterface(service);
Benjamin Franz3362fef2014-11-12 15:57:54 +00001075 mHandler.sendMessage(mHandler.obtainMessage(
1076 MESSAGE_HEADSET_SERVICE_CONNECTED));
The Android Open Source Project33897762009-03-03 19:31:44 -08001077 }
Benjamin Franz3362fef2014-11-12 15:57:54 +00001078 @Override
The Android Open Source Project33897762009-03-03 19:31:44 -08001079 public void onServiceDisconnected(ComponentName className) {
1080 if (DBG) Log.d(TAG, "Proxy object disconnected");
1081 mService = null;
Benjamin Franz3362fef2014-11-12 15:57:54 +00001082 mHandler.sendMessage(mHandler.obtainMessage(
1083 MESSAGE_HEADSET_SERVICE_DISCONNECTED));
The Android Open Source Project33897762009-03-03 19:31:44 -08001084 }
1085 };
The Android Open Source Project0047a0f2009-03-05 20:00:43 -08001086
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001087 private boolean isEnabled() {
1088 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
1089 return false;
1090 }
1091
Jaikumar Ganesh2fbe8f42011-04-06 11:09:30 -07001092 private boolean isDisabled() {
1093 if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
1094 return false;
1095 }
1096
Jaikumar Ganesh2af07762010-08-24 17:36:13 -07001097 private boolean isValidDevice(BluetoothDevice device) {
1098 if (device == null) return false;
1099
1100 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
1101 return false;
1102 }
1103
The Android Open Source Project0047a0f2009-03-05 20:00:43 -08001104 private static void log(String msg) {
1105 Log.d(TAG, msg);
1106 }
Benjamin Franz3362fef2014-11-12 15:57:54 +00001107
1108 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
1109 @Override
1110 public void handleMessage(Message msg) {
1111 switch (msg.what) {
1112 case MESSAGE_HEADSET_SERVICE_CONNECTED: {
1113 if (mServiceListener != null) {
1114 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
1115 BluetoothHeadset.this);
1116 }
1117 break;
1118 }
1119 case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
1120 if (mServiceListener != null) {
1121 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
1122 }
Benjamin Franz3362fef2014-11-12 15:57:54 +00001123 break;
1124 }
1125 }
1126 }
1127 };
The Android Open Source Project33897762009-03-03 19:31:44 -08001128}