blob: 874026fb157d859acf2fb29438b19a46f581238b [file] [log] [blame]
Hemant Gupta7aca90f2013-08-19 19:03:51 +05301/*
2 * Copyright (C) 2014 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
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.os.Bundle;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.util.Log;
27
28import java.util.ArrayList;
29import java.util.List;
30
31/**
32 * Public API to control Hands Free Profile (HFP role only).
33 * <p>
34 * This class defines methods that shall be used by application to manage profile
35 * connection, calls states and calls actions.
36 * <p>
37 *
38 * @hide
39 * */
Mike Lockwoodcf916d32014-06-12 11:23:40 -070040public final class BluetoothHeadsetClient implements BluetoothProfile {
41 private static final String TAG = "BluetoothHeadsetClient";
Hemant Gupta7aca90f2013-08-19 19:03:51 +053042 private static final boolean DBG = true;
43 private static final boolean VDBG = false;
44
45 /**
46 * Intent sent whenever connection to remote changes.
47 *
48 * <p>It includes two extras:
49 * <code>BluetoothProfile.EXTRA_PREVIOUS_STATE</code>
50 * and <code>BluetoothProfile.EXTRA_STATE</code>, which
51 * are mandatory.
52 * <p>There are also non mandatory feature extras:
53 * {@link #EXTRA_AG_FEATURE_3WAY_CALLING},
54 * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION},
55 * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT},
56 * {@link #EXTRA_AG_FEATURE_REJECT_CALL},
57 * {@link #EXTRA_AG_FEATURE_ECC},
58 * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD},
59 * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL},
60 * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL},
61 * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT},
62 * {@link #EXTRA_AG_FEATURE_MERGE},
63 * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH},
64 * sent as boolean values only when <code>EXTRA_STATE</code>
65 * is set to <code>STATE_CONNECTED</code>.</p>
66 *
67 * <p>Note that features supported by AG are being sent as
68 * booleans with value <code>true</code>,
69 * and not supported ones are <strong>not</strong> being sent at all.</p>
70 */
71 public static final String ACTION_CONNECTION_STATE_CHANGED =
Mike Lockwoodcf916d32014-06-12 11:23:40 -070072 "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED";
Hemant Gupta7aca90f2013-08-19 19:03:51 +053073
74 /**
75 * Intent sent whenever audio state changes.
76 *
77 * <p>It includes two mandatory extras:
78 * {@link BluetoothProfile.EXTRA_STATE},
79 * {@link BluetoothProfile.EXTRA_PREVIOUS_STATE},
80 * with possible values:
81 * {@link #STATE_AUDIO_CONNECTING},
82 * {@link #STATE_AUDIO_CONNECTED},
83 * {@link #STATE_AUDIO_DISCONNECTED}</p>
84 * <p>When <code>EXTRA_STATE</code> is set
85 * to </code>STATE_AUDIO_CONNECTED</code>,
86 * it also includes {@link #EXTRA_AUDIO_WBS}
87 * indicating wide band speech support.</p>
88 */
89 public static final String ACTION_AUDIO_STATE_CHANGED =
Mike Lockwoodcf916d32014-06-12 11:23:40 -070090 "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED";
Hemant Gupta7aca90f2013-08-19 19:03:51 +053091
92 /**
93 * Intent sending updates of the Audio Gateway state.
94 * Each extra is being sent only when value it
95 * represents has been changed recently on AG.
96 * <p>It can contain one or more of the following extras:
97 * {@link #EXTRA_NETWORK_STATUS},
98 * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH},
99 * {@link #EXTRA_NETWORK_ROAMING},
100 * {@link #EXTRA_BATTERY_LEVEL},
101 * {@link #EXTRA_OPERATOR_NAME},
102 * {@link #EXTRA_VOICE_RECOGNITION},
103 * {@link #EXTRA_IN_BAND_RING}</p>
104 */
105 public static final String ACTION_AG_EVENT =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700106 "android.bluetooth.headsetclient.profile.action.AG_EVENT";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530107
108 /**
109 * Intent sent whenever state of a call changes.
110 *
111 * <p>It includes:
112 * {@link #EXTRA_CALL},
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700113 * with value of {@link BluetoothHeadsetClientCall} instance,
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530114 * representing actual call state.</p>
115 */
116 public static final String ACTION_CALL_CHANGED =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700117 "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530118
119 /**
120 * Intent that notifies about the result of the last issued action.
121 * Please note that not every action results in explicit action result code being sent.
122 * Instead other notifications about new Audio Gateway state might be sent,
123 * like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value
124 * when for example user started voice recognition from HF unit.
125 */
126 public static final String ACTION_RESULT =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700127 "android.bluetooth.headsetclient.profile.action.RESULT";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530128
129 /**
130 * Intent that notifies about the number attached to the last voice tag
131 * recorded on AG.
132 *
133 * <p>It contains:
134 * {@link #EXTRA_NUMBER},
135 * with a <code>String</code> value representing phone number.</p>
136 */
137 public static final String ACTION_LAST_VTAG =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700138 "android.bluetooth.headsetclient.profile.action.LAST_VTAG";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530139
140 public static final int STATE_AUDIO_DISCONNECTED = 0;
141 public static final int STATE_AUDIO_CONNECTING = 1;
142 public static final int STATE_AUDIO_CONNECTED = 2;
143
144 /**
145 * Extra with information if connected audio is WBS.
146 * <p>Possible values: <code>true</code>,
147 * <code>false</code>.</p>
148 */
149 public static final String EXTRA_AUDIO_WBS =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700150 "android.bluetooth.headsetclient.extra.AUDIO_WBS";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530151
152 /**
153 * Extra for AG_EVENT indicates network status.
154 * <p>Value: 0 - network unavailable,
155 * 1 - network available </p>
156 */
157 public static final String EXTRA_NETWORK_STATUS =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700158 "android.bluetooth.headsetclient.extra.NETWORK_STATUS";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530159 /**
160 * Extra for AG_EVENT intent indicates network signal strength.
161 * <p>Value: <code>Integer</code> representing signal strength.</p>
162 */
163 public static final String EXTRA_NETWORK_SIGNAL_STRENGTH =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700164 "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530165 /**
166 * Extra for AG_EVENT intent indicates roaming state.
167 * <p>Value: 0 - no roaming
168 * 1 - active roaming</p>
169 */
170 public static final String EXTRA_NETWORK_ROAMING =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700171 "android.bluetooth.headsetclient.extra.NETWORK_ROAMING";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530172 /**
173 * Extra for AG_EVENT intent indicates the battery level.
174 * <p>Value: <code>Integer</code> representing signal strength.</p>
175 */
176 public static final String EXTRA_BATTERY_LEVEL =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700177 "android.bluetooth.headsetclient.extra.BATTERY_LEVEL";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530178 /**
179 * Extra for AG_EVENT intent indicates operator name.
180 * <p>Value: <code>String</code> representing operator name.</p>
181 */
182 public static final String EXTRA_OPERATOR_NAME =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700183 "android.bluetooth.headsetclient.extra.OPERATOR_NAME";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530184 /**
185 * Extra for AG_EVENT intent indicates voice recognition state.
186 * <p>Value:
187 * 0 - voice recognition stopped,
188 * 1 - voice recognition started.</p>
189 */
190 public static final String EXTRA_VOICE_RECOGNITION =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700191 "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530192 /**
193 * Extra for AG_EVENT intent indicates in band ring state.
194 * <p>Value:
195 * 0 - in band ring tone not supported, or
196 * 1 - in band ring tone supported.</p>
197 */
198 public static final String EXTRA_IN_BAND_RING =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700199 "android.bluetooth.headsetclient.extra.IN_BAND_RING";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530200
201 /**
202 * Extra for AG_EVENT intent indicates subscriber info.
203 * <p>Value: <code>String</code> containing subscriber information.</p>
204 */
205 public static final String EXTRA_SUBSCRIBER_INFO =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700206 "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530207
208 /**
209 * Extra for AG_CALL_CHANGED intent indicates the
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700210 * {@link BluetoothHeadsetClientCall} object that has changed.
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530211 */
212 public static final String EXTRA_CALL =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700213 "android.bluetooth.headsetclient.extra.CALL";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530214
215 /**
216 * Extra for ACTION_LAST_VTAG intent.
217 * <p>Value: <code>String</code> representing phone number
218 * corresponding to last voice tag recorded on AG</p>
219 */
220 public static final String EXTRA_NUMBER =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700221 "android.bluetooth.headsetclient.extra.NUMBER";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530222
223 /**
224 * Extra for ACTION_RESULT intent that shows the result code of
225 * last issued action.
226 * <p>Possible results:
227 * {@link #ACTION_RESULT_OK},
228 * {@link #ACTION_RESULT_ERROR},
229 * {@link #ACTION_RESULT_ERROR_NO_CARRIER},
230 * {@link #ACTION_RESULT_ERROR_BUSY},
231 * {@link #ACTION_RESULT_ERROR_NO_ANSWER},
232 * {@link #ACTION_RESULT_ERROR_DELAYED},
233 * {@link #ACTION_RESULT_ERROR_BLACKLISTED},
234 * {@link #ACTION_RESULT_ERROR_CME}</p>
235 */
236 public static final String EXTRA_RESULT_CODE =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700237 "android.bluetooth.headsetclient.extra.RESULT_CODE";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530238
239 /**
240 * Extra for ACTION_RESULT intent that shows the extended result code of
241 * last issued action.
242 * <p>Value: <code>Integer</code> - error code.</p>
243 */
244 public static final String EXTRA_CME_CODE =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700245 "android.bluetooth.headsetclient.extra.CME_CODE";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530246
247 /* Extras for AG_FEATURES, extras type is boolean */
248 // TODO verify if all of those are actually useful
249 /**
250 * AG feature: three way calling.
251 */
252 public final static String EXTRA_AG_FEATURE_3WAY_CALLING =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700253 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530254 /**
255 * AG feature: voice recognition.
256 */
257 public final static String EXTRA_AG_FEATURE_VOICE_RECOGNITION =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700258 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530259 /**
260 * AG feature: fetching phone number for voice tagging procedure.
261 */
262 public final static String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700263 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530264 /**
265 * AG feature: ability to reject incoming call.
266 */
267 public final static String EXTRA_AG_FEATURE_REJECT_CALL =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700268 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530269 /**
270 * AG feature: enhanced call handling (terminate specific call, private consultation).
271 */
272 public final static String EXTRA_AG_FEATURE_ECC =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700273 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530274 /**
275 * AG feature: response and hold.
276 */
277 public final static String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700278 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530279 /**
280 * AG call handling feature: accept held or waiting call in three way calling scenarios.
281 */
282 public final static String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700283 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530284 /**
285 * AG call handling feature: release held or waiting call in three way calling scenarios.
286 */
287 public final static String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700288 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530289 /**
290 * AG call handling feature: release active call and accept held or waiting call in three way
291 * calling scenarios.
292 */
293 public final static String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700294 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530295 /**
296 * AG call handling feature: merge two calls, held and active - multi party conference mode.
297 */
298 public final static String EXTRA_AG_FEATURE_MERGE =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700299 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530300 /**
301 * AG call handling feature: merge calls and disconnect from multi party
302 * conversation leaving peers connected to each other.
303 * Note that this feature needs to be supported by mobile network operator
304 * as it requires connection and billing transfer.
305 */
306 public final static String EXTRA_AG_FEATURE_MERGE_AND_DETACH =
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700307 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH";
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530308
309 /* Action result codes */
310 public final static int ACTION_RESULT_OK = 0;
311 public final static int ACTION_RESULT_ERROR = 1;
312 public final static int ACTION_RESULT_ERROR_NO_CARRIER = 2;
313 public final static int ACTION_RESULT_ERROR_BUSY = 3;
314 public final static int ACTION_RESULT_ERROR_NO_ANSWER = 4;
315 public final static int ACTION_RESULT_ERROR_DELAYED = 5;
316 public final static int ACTION_RESULT_ERROR_BLACKLISTED = 6;
317 public final static int ACTION_RESULT_ERROR_CME = 7;
318
319 /* Detailed CME error codes */
320 public final static int CME_PHONE_FAILURE = 0;
321 public final static int CME_NO_CONNECTION_TO_PHONE = 1;
322 public final static int CME_OPERATION_NOT_ALLOWED = 3;
323 public final static int CME_OPERATION_NOT_SUPPORTED = 4;
324 public final static int CME_PHSIM_PIN_REQUIRED = 5;
325 public final static int CME_PHFSIM_PIN_REQUIRED = 6;
326 public final static int CME_PHFSIM_PUK_REQUIRED = 7;
327 public final static int CME_SIM_NOT_INSERTED = 10;
328 public final static int CME_SIM_PIN_REQUIRED = 11;
329 public final static int CME_SIM_PUK_REQUIRED = 12;
330 public final static int CME_SIM_FAILURE = 13;
331 public final static int CME_SIM_BUSY = 14;
332 public final static int CME_SIM_WRONG = 15;
333 public final static int CME_INCORRECT_PASSWORD = 16;
334 public final static int CME_SIM_PIN2_REQUIRED = 17;
335 public final static int CME_SIM_PUK2_REQUIRED = 18;
336 public final static int CME_MEMORY_FULL = 20;
337 public final static int CME_INVALID_INDEX = 21;
338 public final static int CME_NOT_FOUND = 22;
339 public final static int CME_MEMORY_FAILURE = 23;
340 public final static int CME_TEXT_STRING_TOO_LONG = 24;
341 public final static int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25;
342 public final static int CME_DIAL_STRING_TOO_LONG = 26;
343 public final static int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27;
344 public final static int CME_NO_NETWORK_SERVICE = 30;
345 public final static int CME_NETWORK_TIMEOUT = 31;
346 public final static int CME_EMERGENCY_SERVICE_ONLY = 32;
347 public final static int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33;
348 public final static int CME_NOT_SUPPORTED_FOR_VOIP = 34;
349 public final static int CME_SIP_RESPONSE_CODE = 35;
350 public final static int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40;
351 public final static int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41;
352 public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42;
353 public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43;
354 public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44;
355 public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45;
356 public final static int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46;
357 public final static int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47;
358 public final static int CME_HIDDEN_KEY_REQUIRED = 48;
359 public final static int CME_EAP_NOT_SUPPORTED = 49;
360 public final static int CME_INCORRECT_PARAMETERS = 50;
361
362 /* Action policy for other calls when accepting call */
363 public static final int CALL_ACCEPT_NONE = 0;
364 public static final int CALL_ACCEPT_HOLD = 1;
365 public static final int CALL_ACCEPT_TERMINATE = 2;
366
367 private Context mContext;
368 private ServiceListener mServiceListener;
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700369 private IBluetoothHeadsetClient mService;
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530370 private BluetoothAdapter mAdapter;
371
372 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
373 new IBluetoothStateChangeCallback.Stub() {
374 @Override
375 public void onBluetoothStateChange(boolean up) {
376 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
377 if (!up) {
378 if (VDBG) Log.d(TAG,"Unbinding service...");
379 synchronized (mConnection) {
380 try {
381 mService = null;
382 mContext.unbindService(mConnection);
383 } catch (Exception re) {
384 Log.e(TAG,"",re);
385 }
386 }
387 } else {
388 synchronized (mConnection) {
389 try {
390 if (mService == null) {
391 if (VDBG) Log.d(TAG,"Binding service...");
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700392 Intent intent = new Intent(IBluetoothHeadsetClient.class.getName());
393 doBind();
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530394 }
395 } catch (Exception re) {
396 Log.e(TAG,"",re);
397 }
398 }
399 }
400 }
401 };
402
403 /**
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700404 * Create a BluetoothHeadsetClient proxy object.
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530405 */
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700406 /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) {
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530407 mContext = context;
408 mServiceListener = l;
409 mAdapter = BluetoothAdapter.getDefaultAdapter();
410
411 IBluetoothManager mgr = mAdapter.getBluetoothManager();
412 if (mgr != null) {
413 try {
414 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
415 } catch (RemoteException e) {
416 Log.e(TAG,"",e);
417 }
418 }
419
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700420 doBind();
421 }
422
423 boolean doBind() {
424 Intent intent = new Intent(IBluetoothHeadsetClient.class.getName());
425 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
426 intent.setComponent(comp);
427 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
428 android.os.Process.myUserHandle())) {
429 Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
430 return false;
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530431 }
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700432 return true;
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530433 }
434
435 /**
436 * Close the connection to the backing service.
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700437 * Other public functions of BluetoothHeadsetClient will return default error
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530438 * results once close() has been called. Multiple invocations of close()
439 * are ok.
440 */
441 /*package*/ void close() {
442 if (VDBG) log("close()");
443
444 IBluetoothManager mgr = mAdapter.getBluetoothManager();
445 if (mgr != null) {
446 try {
447 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
448 } catch (Exception e) {
449 Log.e(TAG,"",e);
450 }
451 }
452
453 synchronized (mConnection) {
454 if (mService != null) {
455 try {
456 mService = null;
457 mContext.unbindService(mConnection);
458 } catch (Exception re) {
459 Log.e(TAG,"",re);
460 }
461 }
462 }
463 mServiceListener = null;
464 }
465
466 /**
467 * Connects to remote device.
468 *
469 * Currently, the system supports only 1 connection. So, in case of the
470 * second connection, this implementation will disconnect already connected
471 * device automatically and will process the new one.
472 *
473 * @param device a remote device we want connect to
474 * @return <code>true</code> if command has been issued successfully;
475 * <code>false</code> otherwise;
476 * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED}
477 * intent.
478 */
479 public boolean connect(BluetoothDevice device) {
480 if (DBG) log("connect(" + device + ")");
481 if (mService != null && isEnabled() &&
482 isValidDevice(device)) {
483 try {
484 return mService.connect(device);
485 } catch (RemoteException e) {
486 Log.e(TAG, Log.getStackTraceString(new Throwable()));
487 return false;
488 }
489 }
490 if (mService == null) Log.w(TAG, "Proxy not attached to service");
491 return false;
492 }
493
494 /**
495 * Disconnects remote device
496 *
497 * @param device a remote device we want disconnect
498 * @return <code>true</code> if command has been issued successfully;
499 * <code>false</code> otherwise;
500 * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED}
501 * intent.
502 */
503 public boolean disconnect(BluetoothDevice device) {
504 if (DBG) log("disconnect(" + device + ")");
505 if (mService != null && isEnabled() &&
506 isValidDevice(device)) {
507 try {
508 return mService.disconnect(device);
509 } catch (RemoteException e) {
510 Log.e(TAG, Log.getStackTraceString(new Throwable()));
511 return false;
512 }
513 }
514 if (mService == null) Log.w(TAG, "Proxy not attached to service");
515 return false;
516 }
517
518 /**
519 * Return the list of connected remote devices
520 *
521 * @return list of connected devices; empty list if nothing is connected.
522 */
523 @Override
524 public List<BluetoothDevice> getConnectedDevices() {
525 if (VDBG) log("getConnectedDevices()");
526 if (mService != null && isEnabled()) {
527 try {
528 return mService.getConnectedDevices();
529 } catch (RemoteException e) {
530 Log.e(TAG, Log.getStackTraceString(new Throwable()));
531 return new ArrayList<BluetoothDevice>();
532 }
533 }
534 if (mService == null) Log.w(TAG, "Proxy not attached to service");
535 return new ArrayList<BluetoothDevice>();
536 }
537
538 /**
539 * Returns list of remote devices in a particular state
540 *
541 * @param states collection of states
542 * @return list of devices that state matches the states listed in
543 * <code>states</code>; empty list if nothing matches the
544 * <code>states</code>
545 */
546 @Override
547 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
548 if (VDBG) log("getDevicesMatchingStates()");
549 if (mService != null && isEnabled()) {
550 try {
551 return mService.getDevicesMatchingConnectionStates(states);
552 } catch (RemoteException e) {
553 Log.e(TAG, Log.getStackTraceString(new Throwable()));
554 return new ArrayList<BluetoothDevice>();
555 }
556 }
557 if (mService == null) Log.w(TAG, "Proxy not attached to service");
558 return new ArrayList<BluetoothDevice>();
559 }
560
561 /**
562 * Returns state of the <code>device</code>
563 *
564 * @param device a remote device
565 * @return the state of connection of the device
566 */
567 @Override
568 public int getConnectionState(BluetoothDevice device) {
569 if (VDBG) log("getConnectionState(" + device + ")");
570 if (mService != null && isEnabled() &&
571 isValidDevice(device)) {
572 try {
573 return mService.getConnectionState(device);
574 } catch (RemoteException e) {
575 Log.e(TAG, Log.getStackTraceString(new Throwable()));
576 return BluetoothProfile.STATE_DISCONNECTED;
577 }
578 }
579 if (mService == null) Log.w(TAG, "Proxy not attached to service");
580 return BluetoothProfile.STATE_DISCONNECTED;
581 }
582
583 /**
584 * Set priority of the profile
585 *
586 * The device should already be paired.
587 */
588 public boolean setPriority(BluetoothDevice device, int priority) {
589 if (DBG) log("setPriority(" + device + ", " + priority + ")");
590 if (mService != null && isEnabled() &&
591 isValidDevice(device)) {
592 if (priority != BluetoothProfile.PRIORITY_OFF &&
593 priority != BluetoothProfile.PRIORITY_ON) {
594 return false;
595 }
596 try {
597 return mService.setPriority(device, priority);
598 } catch (RemoteException e) {
599 Log.e(TAG, Log.getStackTraceString(new Throwable()));
600 return false;
601 }
602 }
603 if (mService == null) Log.w(TAG, "Proxy not attached to service");
604 return false;
605 }
606
607 /**
608 * Get the priority of the profile.
609 */
610 public int getPriority(BluetoothDevice device) {
611 if (VDBG) log("getPriority(" + device + ")");
612 if (mService != null && isEnabled() &&
613 isValidDevice(device)) {
614 try {
615 return mService.getPriority(device);
616 } catch (RemoteException e) {
617 Log.e(TAG, Log.getStackTraceString(new Throwable()));
618 return PRIORITY_OFF;
619 }
620 }
621 if (mService == null) Log.w(TAG, "Proxy not attached to service");
622 return PRIORITY_OFF;
623 }
624
625 /**
626 * Starts voice recognition.
627 *
628 * @param device remote device
629 * @return <code>true</code> if command has been issued successfully;
630 * <code>false</code> otherwise;
631 * upon completion HFP sends {@link #ACTION_AG_EVENT}
632 * intent.
633 *
634 * <p>Feature required for successful execution is being reported by:
635 * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}.
636 * This method invocation will fail silently when feature is not supported.</p>
637 */
638 public boolean startVoiceRecognition(BluetoothDevice device) {
639 if (DBG) log("startVoiceRecognition()");
640 if (mService != null && isEnabled() &&
641 isValidDevice(device)) {
642 try {
643 return mService.startVoiceRecognition(device);
644 } catch (RemoteException e) {
645 Log.e(TAG, Log.getStackTraceString(new Throwable()));
646 }
647 }
648 if (mService == null) Log.w(TAG, "Proxy not attached to service");
649 return false;
650 }
651
652 /**
653 * Stops voice recognition.
654 *
655 * @param device remote device
656 * @return <code>true</code> if command has been issued successfully;
657 * <code>false</code> otherwise;
658 * upon completion HFP sends {@link #ACTION_AG_EVENT}
659 * intent.
660 *
661 * <p>Feature required for successful execution is being reported by:
662 * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}.
663 * This method invocation will fail silently when feature is not supported.</p>
664 */
665 public boolean stopVoiceRecognition(BluetoothDevice device) {
666 if (DBG) log("stopVoiceRecognition()");
667 if (mService != null && isEnabled() &&
668 isValidDevice(device)) {
669 try {
670 return mService.stopVoiceRecognition(device);
671 } catch (RemoteException e) {
672 Log.e(TAG, Log.getStackTraceString(new Throwable()));
673 }
674 }
675 if (mService == null) Log.w(TAG, "Proxy not attached to service");
676 return false;
677 }
678
679 /**
680 * Returns list of all calls in any state.
681 *
682 * @param device remote device
683 * @return list of calls; empty list if none call exists
684 */
Mike Lockwoodcf916d32014-06-12 11:23:40 -0700685 public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
Hemant Gupta7aca90f2013-08-19 19:03:51 +0530686 if (DBG) log("getCurrentCalls()");
687 if (mService != null && isEnabled() &&
688 isValidDevice(device)) {
689 try {
690 return mService.getCurrentCalls(device);
691 } catch (RemoteException e) {
692 Log.e(TAG, Log.getStackTraceString(new Throwable()));
693 }
694 }
695 if (mService == null) Log.w(TAG, "Proxy not attached to service");
696 return null;
697 }
698
699 /**
700 * Returns list of current values of AG indicators.
701 *
702 * @param device remote device
703 * @return bundle of AG indicators; null if device is not in
704 * CONNECTED state
705 */
706 public Bundle getCurrentAgEvents(BluetoothDevice device) {
707 if (DBG) log("getCurrentCalls()");
708 if (mService != null && isEnabled() &&
709 isValidDevice(device)) {
710 try {
711 return mService.getCurrentAgEvents(device);
712 } catch (RemoteException e) {
713 Log.e(TAG, Log.getStackTraceString(new Throwable()));
714 }
715 }
716 if (mService == null) Log.w(TAG, "Proxy not attached to service");
717 return null;
718 }
719
720 /**
721 * Accepts a call
722 *
723 * @param device remote device
724 * @param flag action policy while accepting a call. Possible values
725 * {@link #CALL_ACCEPT_NONE}, {@link #CALL_ACCEPT_HOLD},
726 * {@link #CALL_ACCEPT_TERMINATE}
727 * @return <code>true</code> if command has been issued successfully;
728 * <code>false</code> otherwise;
729 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
730 * intent.
731 */
732 public boolean acceptCall(BluetoothDevice device, int flag) {
733 if (DBG) log("acceptCall()");
734 if (mService != null && isEnabled() &&
735 isValidDevice(device)) {
736 try {
737 return mService.acceptCall(device, flag);
738 } catch (RemoteException e) {
739 Log.e(TAG, Log.getStackTraceString(new Throwable()));
740 }
741 }
742 if (mService == null) Log.w(TAG, "Proxy not attached to service");
743 return false;
744 }
745
746 /**
747 * Holds a call.
748 *
749 * @param device remote device
750 * @return <code>true</code> if command has been issued successfully;
751 * <code>false</code> otherwise;
752 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
753 * intent.
754 */
755 public boolean holdCall(BluetoothDevice device) {
756 if (DBG) log("holdCall()");
757 if (mService != null && isEnabled() &&
758 isValidDevice(device)) {
759 try {
760 return mService.holdCall(device);
761 } catch (RemoteException e) {
762 Log.e(TAG, Log.getStackTraceString(new Throwable()));
763 }
764 }
765 if (mService == null) Log.w(TAG, "Proxy not attached to service");
766 return false;
767 }
768
769 /**
770 * Rejects a call.
771 *
772 * @param device remote device
773 * @return <code>true</code> if command has been issued successfully;
774 * <code>false</code> otherwise;
775 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
776 * intent.
777 *
778 * <p>Feature required for successful execution is being reported by:
779 * {@link #EXTRA_AG_FEATURE_REJECT_CALL}.
780 * This method invocation will fail silently when feature is not supported.</p>
781 */
782 public boolean rejectCall(BluetoothDevice device) {
783 if (DBG) log("rejectCall()");
784 if (mService != null && isEnabled() &&
785 isValidDevice(device)) {
786 try {
787 return mService.rejectCall(device);
788 } catch (RemoteException e) {
789 Log.e(TAG, Log.getStackTraceString(new Throwable()));
790 }
791 }
792 if (mService == null) Log.w(TAG, "Proxy not attached to service");
793 return false;
794 }
795
796 /**
797 * Terminates a specified call.
798 *
799 * Works only when Extended Call Control is supported by Audio Gateway.
800 *
801 * @param device remote device
802 * @param index index of the call to be terminated
803 * @return <code>true</code> if command has been issued successfully;
804 * <code>false</code> otherwise;
805 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
806 * intent.
807 *
808 * <p>Feature required for successful execution is being reported by:
809 * {@link #EXTRA_AG_FEATURE_ECC}.
810 * This method invocation will fail silently when feature is not supported.</p>
811 */
812 public boolean terminateCall(BluetoothDevice device, int index) {
813 if (DBG) log("terminateCall()");
814 if (mService != null && isEnabled() &&
815 isValidDevice(device)) {
816 try {
817 return mService.terminateCall(device, index);
818 } catch (RemoteException e) {
819 Log.e(TAG, Log.getStackTraceString(new Throwable()));
820 }
821 }
822 if (mService == null) Log.w(TAG, "Proxy not attached to service");
823 return false;
824 }
825
826 /**
827 * Enters private mode with a specified call.
828 *
829 * Works only when Extended Call Control is supported by Audio Gateway.
830 *
831 * @param device remote device
832 * @param index index of the call to connect in private mode
833 * @return <code>true</code> if command has been issued successfully;
834 * <code>false</code> otherwise;
835 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
836 * intent.
837 *
838 * <p>Feature required for successful execution is being reported by:
839 * {@link #EXTRA_AG_FEATURE_ECC}.
840 * This method invocation will fail silently when feature is not supported.</p>
841 */
842 public boolean enterPrivateMode(BluetoothDevice device, int index) {
843 if (DBG) log("enterPrivateMode()");
844 if (mService != null && isEnabled() &&
845 isValidDevice(device)) {
846 try {
847 return mService.enterPrivateMode(device, index);
848 } catch (RemoteException e) {
849 Log.e(TAG, Log.getStackTraceString(new Throwable()));
850 }
851 }
852 if (mService == null) Log.w(TAG, "Proxy not attached to service");
853 return false;
854 }
855
856 /**
857 * Performs explicit call transfer.
858 *
859 * That means connect other calls and disconnect.
860 *
861 * @param device remote device
862 * @return <code>true</code> if command has been issued successfully;
863 * <code>false</code> otherwise;
864 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
865 * intent.
866 *
867 * <p>Feature required for successful execution is being reported by:
868 * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}.
869 * This method invocation will fail silently when feature is not supported.</p>
870 */
871 public boolean explicitCallTransfer(BluetoothDevice device) {
872 if (DBG) log("explicitCallTransfer()");
873 if (mService != null && isEnabled() &&
874 isValidDevice(device)) {
875 try {
876 return mService.explicitCallTransfer(device);
877 } catch (RemoteException e) {
878 Log.e(TAG, Log.getStackTraceString(new Throwable()));
879 }
880 }
881 if (mService == null) Log.w(TAG, "Proxy not attached to service");
882 return false;
883 }
884
885 /**
886 * Redials last number from Audio Gateway.
887 *
888 * @param device remote device
889 * @return <code>true</code> if command has been issued successfully;
890 * <code>false</code> otherwise;
891 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
892 * intent in case of success; {@link #ACTION_RESULT} is sent
893 * otherwise;
894 */
895 public boolean redial(BluetoothDevice device) {
896 if (DBG) log("redial()");
897 if (mService != null && isEnabled() &&
898 isValidDevice(device)) {
899 try {
900 return mService.redial(device);
901 } catch (RemoteException e) {
902 Log.e(TAG, Log.getStackTraceString(new Throwable()));
903 }
904 }
905 if (mService == null) Log.w(TAG, "Proxy not attached to service");
906 return false;
907 }
908
909 /**
910 * Places a call with specified number.
911 *
912 * @param device remote device
913 * @param number valid phone number
914 * @return <code>true</code> if command has been issued successfully;
915 * <code>false</code> otherwise;
916 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
917 * intent in case of success; {@link #ACTION_RESULT} is sent
918 * otherwise;
919 */
920 public boolean dial(BluetoothDevice device, String number) {
921 if (DBG) log("dial()");
922 if (mService != null && isEnabled() &&
923 isValidDevice(device)) {
924 try {
925 return mService.dial(device, number);
926 } catch (RemoteException e) {
927 Log.e(TAG, Log.getStackTraceString(new Throwable()));
928 }
929 }
930 if (mService == null) Log.w(TAG, "Proxy not attached to service");
931 return false;
932 }
933
934 /**
935 * Places a call to the number under specified memory location.
936 *
937 * @param device remote device
938 * @param location valid memory location
939 * @return <code>true</code> if command has been issued successfully;
940 * <code>false</code> otherwise;
941 * upon completion HFP sends {@link #ACTION_CALL_CHANGED}
942 * intent in case of success; {@link #ACTION_RESULT} is sent
943 * otherwise;
944 */
945 public boolean dialMemory(BluetoothDevice device, int location) {
946 if (DBG) log("dialMemory()");
947 if (mService != null && isEnabled() &&
948 isValidDevice(device)) {
949 try {
950 return mService.dialMemory(device, location);
951 } catch (RemoteException e) {
952 Log.e(TAG, Log.getStackTraceString(new Throwable()));
953 }
954 }
955 if (mService == null) Log.w(TAG, "Proxy not attached to service");
956 return false;
957 }
958
959 /**
960 * Sends DTMF code.
961 *
962 * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,#
963 *
964 * @param device remote device
965 * @param code ASCII code
966 * @return <code>true</code> if command has been issued successfully;
967 * <code>false</code> otherwise;
968 * upon completion HFP sends {@link #ACTION_RESULT} intent;
969 */
970 public boolean sendDTMF(BluetoothDevice device, byte code) {
971 if (DBG) log("sendDTMF()");
972 if (mService != null && isEnabled() &&
973 isValidDevice(device)) {
974 try {
975 return mService.sendDTMF(device, code);
976 } catch (RemoteException e) {
977 Log.e(TAG, Log.getStackTraceString(new Throwable()));
978 }
979 }
980 if (mService == null) Log.w(TAG, "Proxy not attached to service");
981 return false;
982 }
983
984 /**
985 * Get a number corresponding to last voice tag recorded on AG.
986 *
987 * @param device remote device
988 * @return <code>true</code> if command has been issued successfully;
989 * <code>false</code> otherwise;
990 * upon completion HFP sends {@link #ACTION_LAST_VTAG}
991 * or {@link #ACTION_RESULT} intent;
992 *
993 * <p>Feature required for successful execution is being reported by:
994 * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}.
995 * This method invocation will fail silently when feature is not supported.</p>
996 */
997 public boolean getLastVoiceTagNumber(BluetoothDevice device) {
998 if (DBG) log("getLastVoiceTagNumber()");
999 if (mService != null && isEnabled() &&
1000 isValidDevice(device)) {
1001 try {
1002 return mService.getLastVoiceTagNumber(device);
1003 } catch (RemoteException e) {
1004 Log.e(TAG, Log.getStackTraceString(new Throwable()));
1005 }
1006 }
1007 if (mService == null) Log.w(TAG, "Proxy not attached to service");
1008 return false;
1009 }
1010
1011 /**
1012 * Accept the incoming connection.
1013 */
1014 public boolean acceptIncomingConnect(BluetoothDevice device) {
1015 if (DBG) log("acceptIncomingConnect");
1016 if (mService != null && isEnabled()) {
1017 try {
1018 return mService.acceptIncomingConnect(device);
1019 } catch (RemoteException e) {Log.e(TAG, e.toString());}
1020 } else {
1021 Log.w(TAG, "Proxy not attached to service");
1022 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1023 }
1024 return false;
1025 }
1026
1027 /**
1028 * Reject the incoming connection.
1029 */
1030 public boolean rejectIncomingConnect(BluetoothDevice device) {
1031 if (DBG) log("rejectIncomingConnect");
1032 if (mService != null) {
1033 try {
1034 return mService.rejectIncomingConnect(device);
1035 } catch (RemoteException e) {Log.e(TAG, e.toString());}
1036 } else {
1037 Log.w(TAG, "Proxy not attached to service");
1038 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1039 }
1040 return false;
1041 }
1042
1043 /**
1044 * Returns current audio state of Audio Gateway.
1045 *
1046 * Note: This is an internal function and shouldn't be exposed
1047 */
1048 public int getAudioState(BluetoothDevice device) {
1049 if (VDBG) log("getAudioState");
1050 if (mService != null && isEnabled()) {
1051 try {
1052 return mService.getAudioState(device);
1053 } catch (RemoteException e) {Log.e(TAG, e.toString());}
1054 } else {
1055 Log.w(TAG, "Proxy not attached to service");
1056 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1057 }
Mike Lockwoodcf916d32014-06-12 11:23:40 -07001058 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
Hemant Gupta7aca90f2013-08-19 19:03:51 +05301059 }
1060
1061 /**
Bryce Leedc133822015-10-28 22:21:54 -07001062 * Sets whether audio routing is allowed.
1063 *
1064 * Note: This is an internal function and shouldn't be exposed
1065 */
1066 public void setAudioRouteAllowed(boolean allowed) {
1067 if (VDBG) log("setAudioRouteAllowed");
1068 if (mService != null && isEnabled()) {
1069 try {
1070 mService.setAudioRouteAllowed(allowed);
1071 } catch (RemoteException e) {Log.e(TAG, e.toString());}
1072 } else {
1073 Log.w(TAG, "Proxy not attached to service");
1074 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1075 }
1076 }
1077
1078 /**
1079 * Returns whether audio routing is allowed.
1080 *
1081 * Note: This is an internal function and shouldn't be exposed
1082 */
1083 public boolean getAudioRouteAllowed() {
1084 if (VDBG) log("getAudioRouteAllowed");
1085 if (mService != null && isEnabled()) {
1086 try {
1087 return mService.getAudioRouteAllowed();
1088 } catch (RemoteException e) {Log.e(TAG, e.toString());}
1089 } else {
1090 Log.w(TAG, "Proxy not attached to service");
1091 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1092 }
1093 return false;
1094 }
1095
1096 /**
Hemant Gupta7aca90f2013-08-19 19:03:51 +05301097 * Initiates a connection of audio channel.
1098 *
1099 * It setup SCO channel with remote connected Handsfree AG device.
1100 *
1101 * @return <code>true</code> if command has been issued successfully;
1102 * <code>false</code> otherwise;
1103 * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
1104 * intent;
1105 */
1106 public boolean connectAudio() {
1107 if (mService != null && isEnabled()) {
1108 try {
1109 return mService.connectAudio();
1110 } catch (RemoteException e) {
1111 Log.e(TAG, e.toString());
1112 }
1113 } else {
1114 Log.w(TAG, "Proxy not attached to service");
1115 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1116 }
1117 return false;
1118 }
1119
1120 /**
1121 * Disconnects audio channel.
1122 *
1123 * It tears down the SCO channel from remote AG device.
1124 *
1125 * @return <code>true</code> if command has been issued successfully;
1126 * <code>false</code> otherwise;
1127 * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
1128 * intent;
1129 */
1130 public boolean disconnectAudio() {
1131 if (mService != null && isEnabled()) {
1132 try {
1133 return mService.disconnectAudio();
1134 } catch (RemoteException e) {
1135 Log.e(TAG, e.toString());
1136 }
1137 } else {
1138 Log.w(TAG, "Proxy not attached to service");
1139 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1140 }
1141 return false;
1142 }
1143
1144 /**
1145 * Get Audio Gateway features
1146 *
1147 * @param device remote device
1148 * @return bundle of AG features; null if no service or
1149 * AG not connected
1150 */
1151 public Bundle getCurrentAgFeatures(BluetoothDevice device) {
1152 if (mService != null && isEnabled()) {
1153 try {
1154 return mService.getCurrentAgFeatures(device);
1155 } catch (RemoteException e) {
1156 Log.e(TAG, e.toString());
1157 }
1158 } else {
1159 Log.w(TAG, "Proxy not attached to service");
1160 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
1161 }
1162 return null;
1163 }
1164
1165
1166 private ServiceConnection mConnection = new ServiceConnection() {
1167 @Override
1168 public void onServiceConnected(ComponentName className, IBinder service) {
1169 if (DBG) Log.d(TAG, "Proxy object connected");
Mike Lockwoodcf916d32014-06-12 11:23:40 -07001170 mService = IBluetoothHeadsetClient.Stub.asInterface(service);
Hemant Gupta7aca90f2013-08-19 19:03:51 +05301171
1172 if (mServiceListener != null) {
Mike Lockwoodcf916d32014-06-12 11:23:40 -07001173 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT,
1174 BluetoothHeadsetClient.this);
Hemant Gupta7aca90f2013-08-19 19:03:51 +05301175 }
1176 }
1177 @Override
1178 public void onServiceDisconnected(ComponentName className) {
1179 if (DBG) Log.d(TAG, "Proxy object disconnected");
1180 mService = null;
1181 if (mServiceListener != null) {
Mike Lockwoodcf916d32014-06-12 11:23:40 -07001182 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT);
Hemant Gupta7aca90f2013-08-19 19:03:51 +05301183 }
1184 }
1185 };
1186
1187 private boolean isEnabled() {
1188 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
1189 return false;
1190 }
1191
1192 private boolean isValidDevice(BluetoothDevice device) {
1193 if (device == null) return false;
1194
1195 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
1196 return false;
1197 }
1198
1199 private static void log(String msg) {
1200 Log.d(TAG, msg);
1201 }
1202}