blob: 95e61b6f6aced46af7c4ce6e411a7c30989a802a [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;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.os.RemoteException;
26import android.os.IBinder;
27import android.util.Log;
28
29/**
30 * The Android Bluetooth API is not finalized, and *will* change. Use at your
31 * own risk.
32 *
33 * Public API for controlling the Bluetooth Headset Service. This includes both
34 * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will
35 * attempt a handsfree connection first, and fall back to headset.
36 *
37 * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
38 * Service via IPC.
39 *
40 * Creating a BluetoothHeadset object will create a binding with the
41 * BluetoothHeadset service. Users of this object should call close() when they
42 * are finished with the BluetoothHeadset, so that this proxy object can unbind
43 * from the service.
44 *
45 * This BluetoothHeadset object is not immediately bound to the
46 * BluetoothHeadset service. Use the ServiceListener interface to obtain a
47 * notification when it is bound, this is especially important if you wish to
48 * immediately call methods on BluetootHeadset after construction.
49 *
50 * Android only supports one connected Bluetooth Headset at a time.
51 *
52 * @hide
53 */
Nick Pelly2d664882009-08-14 18:33:38 -070054public final class BluetoothHeadset {
The Android Open Source Project33897762009-03-03 19:31:44 -080055
56 private static final String TAG = "BluetoothHeadset";
57 private static final boolean DBG = false;
58
Nick Pellydac4c0d2009-09-10 10:21:56 -070059 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
60 public static final String ACTION_STATE_CHANGED =
61 "android.bluetooth.headset.action.STATE_CHANGED";
62 /**
63 * TODO(API release): Consider incorporating as new state in
64 * HEADSET_STATE_CHANGED
65 */
66 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
67 public static final String ACTION_AUDIO_STATE_CHANGED =
68 "android.bluetooth.headset.action.AUDIO_STATE_CHANGED";
69 public static final String EXTRA_STATE =
70 "android.bluetooth.headset.extra.STATE";
71 public static final String EXTRA_PREVIOUS_STATE =
72 "android.bluetooth.headset.extra.PREVIOUS_STATE";
73 public static final String EXTRA_AUDIO_STATE =
74 "android.bluetooth.headset.extra.AUDIO_STATE";
75
Jaikumar Ganeshf48cda52010-04-02 14:44:43 -070076 /** Extra to be used with the Headset State change intent.
77 * This will be used only when Headset state changes to
78 * {@link #STATE_DISCONNECTED} from any previous state.
79 * This extra field is optional and will be used when
80 * we have deterministic information regarding whether
81 * the disconnect was initiated by the remote device or
82 * by the local adapter.
83 */
84 public static final String EXTRA_DISCONNECT_INITIATOR =
85 "android.bluetooth.headset.extra.DISCONNECT_INITIATOR";
86
Nick Pellydac4c0d2009-09-10 10:21:56 -070087 /**
88 * TODO(API release): Consider incorporating as new state in
89 * HEADSET_STATE_CHANGED
90 */
The Android Open Source Project33897762009-03-03 19:31:44 -080091 private IBluetoothHeadset mService;
92 private final Context mContext;
93 private final ServiceListener mServiceListener;
94
95 /** There was an error trying to obtain the state */
96 public static final int STATE_ERROR = -1;
97 /** No headset currently connected */
98 public static final int STATE_DISCONNECTED = 0;
99 /** Connection attempt in progress */
100 public static final int STATE_CONNECTING = 1;
101 /** A headset is currently connected */
102 public static final int STATE_CONNECTED = 2;
103
The Android Open Source Project2eb744e2009-03-13 13:04:22 -0700104 /** A SCO audio channel is not established */
105 public static final int AUDIO_STATE_DISCONNECTED = 0;
106 /** A SCO audio channel is established */
107 public static final int AUDIO_STATE_CONNECTED = 1;
108
The Android Open Source Project33897762009-03-03 19:31:44 -0800109 public static final int RESULT_FAILURE = 0;
110 public static final int RESULT_SUCCESS = 1;
111 /** Connection canceled before completetion. */
112 public static final int RESULT_CANCELED = 2;
113
Jaikumar Ganeshf48cda52010-04-02 14:44:43 -0700114 /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */
115 public static final int REMOTE_DISCONNECT = 0;
116 public static final int LOCAL_DISCONNECT = 1;
117
118
Jaikumar Ganesh2fafca32009-11-20 15:21:47 -0800119 /** Default priority for headsets that for which we will accept
120 * inconing connections and auto-connect */
121 public static final int PRIORITY_AUTO_CONNECT = 1000;
122 /** Default priority for headsets that for which we will accept
123 * inconing connections but not auto-connect */
124 public static final int PRIORITY_ON = 100;
125 /** Default priority for headsets that should not be auto-connected
126 * and not allow incoming connections. */
The Android Open Source Project33897762009-03-03 19:31:44 -0800127 public static final int PRIORITY_OFF = 0;
Jaikumar Ganesh32272862009-12-11 12:00:31 -0800128 /** Default priority when not set or when the device is unpaired */
129 public static final int PRIORITY_UNDEFINED = -1;
The Android Open Source Project33897762009-03-03 19:31:44 -0800130
131 /**
132 * An interface for notifying BluetoothHeadset IPC clients when they have
133 * been connected to the BluetoothHeadset service.
134 */
135 public interface ServiceListener {
136 /**
137 * Called to notify the client when this proxy object has been
138 * connected to the BluetoothHeadset service. Clients must wait for
139 * this callback before making IPC calls on the BluetoothHeadset
140 * service.
141 */
142 public void onServiceConnected();
143
144 /**
145 * Called to notify the client that this proxy object has been
146 * disconnected from the BluetoothHeadset service. Clients must not
147 * make IPC calls on the BluetoothHeadset service after this callback.
148 * This callback will currently only occur if the application hosting
149 * the BluetoothHeadset service, but may be called more often in future.
150 */
151 public void onServiceDisconnected();
152 }
153
154 /**
155 * Create a BluetoothHeadset proxy object.
156 */
157 public BluetoothHeadset(Context context, ServiceListener l) {
158 mContext = context;
159 mServiceListener = l;
160 if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
161 Log.e(TAG, "Could not bind to Bluetooth Headset Service");
162 }
163 }
164
165 protected void finalize() throws Throwable {
166 try {
167 close();
168 } finally {
169 super.finalize();
170 }
171 }
172
173 /**
174 * Close the connection to the backing service.
175 * Other public functions of BluetoothHeadset will return default error
176 * results once close() has been called. Multiple invocations of close()
177 * are ok.
178 */
179 public synchronized void close() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800180 if (DBG) log("close()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800181 if (mConnection != null) {
182 mContext.unbindService(mConnection);
183 mConnection = null;
184 }
185 }
186
187 /**
188 * Get the current state of the Bluetooth Headset service.
189 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
190 * object is currently not connected to the Headset service.
191 */
192 public int getState() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800193 if (DBG) log("getState()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800194 if (mService != null) {
195 try {
196 return mService.getState();
197 } catch (RemoteException e) {Log.e(TAG, e.toString());}
198 } else {
199 Log.w(TAG, "Proxy not attached to service");
200 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
201 }
202 return BluetoothHeadset.STATE_ERROR;
203 }
204
205 /**
Nick Pelly2d664882009-08-14 18:33:38 -0700206 * Get the BluetoothDevice for the current headset.
207 * @return current headset, or null if not in connected or connecting
The Android Open Source Project33897762009-03-03 19:31:44 -0800208 * state, or if this proxy object is not connected to the Headset
209 * service.
210 */
Nick Pelly2d664882009-08-14 18:33:38 -0700211 public BluetoothDevice getCurrentHeadset() {
212 if (DBG) log("getCurrentHeadset()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800213 if (mService != null) {
214 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700215 return mService.getCurrentHeadset();
The Android Open Source Project33897762009-03-03 19:31:44 -0800216 } catch (RemoteException e) {Log.e(TAG, e.toString());}
217 } else {
218 Log.w(TAG, "Proxy not attached to service");
219 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
220 }
221 return null;
222 }
223
224 /**
225 * Request to initiate a connection to a headset.
226 * This call does not block. Fails if a headset is already connecting
227 * or connected.
Nick Pelly2d664882009-08-14 18:33:38 -0700228 * Initiates auto-connection if device is null. Tries to connect to all
The Android Open Source Project33897762009-03-03 19:31:44 -0800229 * devices with priority greater than PRIORITY_AUTO in descending order.
Nick Pelly2d664882009-08-14 18:33:38 -0700230 * @param device device to connect to, or null to auto-connect last connected
231 * headset
232 * @return false if there was a problem initiating the connection
233 * procedure, and no further HEADSET_STATE_CHANGED intents
234 * will be expected.
The Android Open Source Project33897762009-03-03 19:31:44 -0800235 */
Nick Pelly2d664882009-08-14 18:33:38 -0700236 public boolean connectHeadset(BluetoothDevice device) {
237 if (DBG) log("connectHeadset(" + device + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800238 if (mService != null) {
239 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700240 if (mService.connectHeadset(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800241 return true;
242 }
243 } catch (RemoteException e) {Log.e(TAG, e.toString());}
244 } else {
245 Log.w(TAG, "Proxy not attached to service");
246 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
247 }
248 return false;
249 }
250
251 /**
252 * Returns true if the specified headset is connected (does not include
253 * connecting). Returns false if not connected, or if this proxy object
254 * if not currently connected to the headset service.
255 */
Nick Pelly2d664882009-08-14 18:33:38 -0700256 public boolean isConnected(BluetoothDevice device) {
257 if (DBG) log("isConnected(" + device + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800258 if (mService != null) {
259 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700260 return mService.isConnected(device);
The Android Open Source Project33897762009-03-03 19:31:44 -0800261 } catch (RemoteException e) {Log.e(TAG, e.toString());}
262 } else {
263 Log.w(TAG, "Proxy not attached to service");
264 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
265 }
266 return false;
267 }
268
269 /**
270 * Disconnects the current headset. Currently this call blocks, it may soon
271 * be made asynchornous. Returns false if this proxy object is
272 * not currently connected to the Headset service.
273 */
274 public boolean disconnectHeadset() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800275 if (DBG) log("disconnectHeadset()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800276 if (mService != null) {
277 try {
278 mService.disconnectHeadset();
279 return true;
280 } catch (RemoteException e) {Log.e(TAG, e.toString());}
281 } else {
282 Log.w(TAG, "Proxy not attached to service");
283 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
284 }
285 return false;
286 }
287
288 /**
289 * Start BT Voice Recognition mode, and set up Bluetooth audio path.
290 * Returns false if there is no headset connected, or if the
291 * connected headset does not support voice recognition, or on
292 * error.
293 */
294 public boolean startVoiceRecognition() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800295 if (DBG) log("startVoiceRecognition()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800296 if (mService != null) {
297 try {
298 return mService.startVoiceRecognition();
299 } catch (RemoteException e) {Log.e(TAG, e.toString());}
300 } else {
301 Log.w(TAG, "Proxy not attached to service");
302 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
303 }
304 return false;
305 }
306
307 /**
308 * Stop BT Voice Recognition mode, and shut down Bluetooth audio path.
309 * Returns false if there is no headset connected, or the connected
310 * headset is not in voice recognition mode, or on error.
311 */
312 public boolean stopVoiceRecognition() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800313 if (DBG) log("stopVoiceRecognition()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800314 if (mService != null) {
315 try {
316 return mService.stopVoiceRecognition();
317 } catch (RemoteException e) {Log.e(TAG, e.toString());}
318 } else {
319 Log.w(TAG, "Proxy not attached to service");
320 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
321 }
322 return false;
323 }
324
325 /**
326 * Set priority of headset.
327 * Priority is a non-negative integer. By default paired headsets will have
328 * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
329 * Headsets with priority greater than zero will be auto-connected, and
330 * incoming connections will be accepted (if no other headset is
331 * connected).
332 * Auto-connection occurs at the following events: boot, incoming phone
333 * call, outgoing phone call.
334 * Headsets with priority equal to zero, or that are unpaired, are not
335 * auto-connected.
336 * Incoming connections are ignored regardless of priority if there is
337 * already a headset connected.
Nick Pelly2d664882009-08-14 18:33:38 -0700338 * @param device paired headset
The Android Open Source Project33897762009-03-03 19:31:44 -0800339 * @param priority Integer priority, for example PRIORITY_AUTO or
340 * PRIORITY_NONE
Nick Pelly2d664882009-08-14 18:33:38 -0700341 * @return true if successful, false if there was some error
The Android Open Source Project33897762009-03-03 19:31:44 -0800342 */
Nick Pelly2d664882009-08-14 18:33:38 -0700343 public boolean setPriority(BluetoothDevice device, int priority) {
344 if (DBG) log("setPriority(" + device + ", " + priority + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800345 if (mService != null) {
346 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700347 return mService.setPriority(device, priority);
The Android Open Source Project33897762009-03-03 19:31:44 -0800348 } catch (RemoteException e) {Log.e(TAG, e.toString());}
349 } else {
350 Log.w(TAG, "Proxy not attached to service");
351 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
352 }
353 return false;
354 }
355
356 /**
357 * Get priority of headset.
Nick Pelly2d664882009-08-14 18:33:38 -0700358 * @param device headset
359 * @return non-negative priority, or negative error code on error
The Android Open Source Project33897762009-03-03 19:31:44 -0800360 */
Nick Pelly2d664882009-08-14 18:33:38 -0700361 public int getPriority(BluetoothDevice device) {
362 if (DBG) log("getPriority(" + device + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800363 if (mService != null) {
364 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700365 return mService.getPriority(device);
The Android Open Source Project33897762009-03-03 19:31:44 -0800366 } catch (RemoteException e) {Log.e(TAG, e.toString());}
367 } else {
368 Log.w(TAG, "Proxy not attached to service");
369 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
370 }
371 return -1;
372 }
373
374 /**
Nick Pelly312cd3a2009-06-19 10:08:09 -0700375 * Get battery usage hint for Bluetooth Headset service.
376 * This is a monotonically increasing integer. Wraps to 0 at
377 * Integer.MAX_INT, and at boot.
378 * Current implementation returns the number of AT commands handled since
379 * boot. This is a good indicator for spammy headset/handsfree units that
380 * can keep the device awake by polling for cellular status updates. As a
381 * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
382 * @return monotonically increasing battery usage hint, or a negative error
383 * code on error
384 * @hide
385 */
386 public int getBatteryUsageHint() {
387 if (DBG) log("getBatteryUsageHint()");
388 if (mService != null) {
389 try {
390 return mService.getBatteryUsageHint();
391 } catch (RemoteException e) {Log.e(TAG, e.toString());}
392 } else {
393 Log.w(TAG, "Proxy not attached to service");
394 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
395 }
396 return -1;
397 }
398
Eric Laurent2e66fa22010-03-17 14:59:27 -0700399 /**
400 * Indicates if current platform supports voice dialing over bluetooth SCO.
401 * @return true if voice dialing over bluetooth is supported, false otherwise.
402 * @hide
403 */
404 public static boolean isBluetoothVoiceDialingEnabled(Context context) {
405 return context.getResources().getBoolean(
406 com.android.internal.R.bool.config_bluetooth_sco_off_call);
407 }
408
The Android Open Source Project33897762009-03-03 19:31:44 -0800409 private ServiceConnection mConnection = new ServiceConnection() {
410 public void onServiceConnected(ComponentName className, IBinder service) {
411 if (DBG) Log.d(TAG, "Proxy object connected");
412 mService = IBluetoothHeadset.Stub.asInterface(service);
413 if (mServiceListener != null) {
414 mServiceListener.onServiceConnected();
415 }
416 }
417 public void onServiceDisconnected(ComponentName className) {
418 if (DBG) Log.d(TAG, "Proxy object disconnected");
419 mService = null;
420 if (mServiceListener != null) {
421 mServiceListener.onServiceDisconnected();
422 }
423 }
424 };
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800425
426 private static void log(String msg) {
427 Log.d(TAG, msg);
428 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800429}