blob: 0e3d2bbb19290afd6caf1af19c14ee8e3b7153a5 [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
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.os.RemoteException;
24import android.os.IBinder;
25import android.util.Log;
26
27/**
28 * The Android Bluetooth API is not finalized, and *will* change. Use at your
29 * own risk.
30 *
31 * Public API for controlling the Bluetooth Headset Service. This includes both
32 * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will
33 * attempt a handsfree connection first, and fall back to headset.
34 *
35 * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
36 * Service via IPC.
37 *
38 * Creating a BluetoothHeadset object will create a binding with the
39 * BluetoothHeadset service. Users of this object should call close() when they
40 * are finished with the BluetoothHeadset, so that this proxy object can unbind
41 * from the service.
42 *
43 * This BluetoothHeadset object is not immediately bound to the
44 * BluetoothHeadset service. Use the ServiceListener interface to obtain a
45 * notification when it is bound, this is especially important if you wish to
46 * immediately call methods on BluetootHeadset after construction.
47 *
48 * Android only supports one connected Bluetooth Headset at a time.
49 *
50 * @hide
51 */
Nick Pelly2d664882009-08-14 18:33:38 -070052public final class BluetoothHeadset {
The Android Open Source Project33897762009-03-03 19:31:44 -080053
54 private static final String TAG = "BluetoothHeadset";
55 private static final boolean DBG = false;
56
57 private IBluetoothHeadset mService;
58 private final Context mContext;
59 private final ServiceListener mServiceListener;
60
61 /** There was an error trying to obtain the state */
62 public static final int STATE_ERROR = -1;
63 /** No headset currently connected */
64 public static final int STATE_DISCONNECTED = 0;
65 /** Connection attempt in progress */
66 public static final int STATE_CONNECTING = 1;
67 /** A headset is currently connected */
68 public static final int STATE_CONNECTED = 2;
69
The Android Open Source Project2eb744e2009-03-13 13:04:22 -070070 /** A SCO audio channel is not established */
71 public static final int AUDIO_STATE_DISCONNECTED = 0;
72 /** A SCO audio channel is established */
73 public static final int AUDIO_STATE_CONNECTED = 1;
74
The Android Open Source Project33897762009-03-03 19:31:44 -080075 public static final int RESULT_FAILURE = 0;
76 public static final int RESULT_SUCCESS = 1;
77 /** Connection canceled before completetion. */
78 public static final int RESULT_CANCELED = 2;
79
80 /** Default priority for headsets that should be auto-connected */
81 public static final int PRIORITY_AUTO = 100;
82 /** Default priority for headsets that should not be auto-connected */
83 public static final int PRIORITY_OFF = 0;
84
Nick Pelly1b5300f2009-03-25 17:33:56 -070085 /** The voice dialer 'works' but the user experience is poor. The voice
86 * recognizer has trouble dealing with the 8kHz SCO signal, and it still
87 * requires visual confirmation. Disable for cupcake.
88 */
89 public static final boolean DISABLE_BT_VOICE_DIALING = true;
90
The Android Open Source Project33897762009-03-03 19:31:44 -080091 /**
92 * An interface for notifying BluetoothHeadset IPC clients when they have
93 * been connected to the BluetoothHeadset service.
94 */
95 public interface ServiceListener {
96 /**
97 * Called to notify the client when this proxy object has been
98 * connected to the BluetoothHeadset service. Clients must wait for
99 * this callback before making IPC calls on the BluetoothHeadset
100 * service.
101 */
102 public void onServiceConnected();
103
104 /**
105 * Called to notify the client that this proxy object has been
106 * disconnected from the BluetoothHeadset service. Clients must not
107 * make IPC calls on the BluetoothHeadset service after this callback.
108 * This callback will currently only occur if the application hosting
109 * the BluetoothHeadset service, but may be called more often in future.
110 */
111 public void onServiceDisconnected();
112 }
113
114 /**
115 * Create a BluetoothHeadset proxy object.
116 */
117 public BluetoothHeadset(Context context, ServiceListener l) {
118 mContext = context;
119 mServiceListener = l;
120 if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
121 Log.e(TAG, "Could not bind to Bluetooth Headset Service");
122 }
123 }
124
125 protected void finalize() throws Throwable {
126 try {
127 close();
128 } finally {
129 super.finalize();
130 }
131 }
132
133 /**
134 * Close the connection to the backing service.
135 * Other public functions of BluetoothHeadset will return default error
136 * results once close() has been called. Multiple invocations of close()
137 * are ok.
138 */
139 public synchronized void close() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800140 if (DBG) log("close()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800141 if (mConnection != null) {
142 mContext.unbindService(mConnection);
143 mConnection = null;
144 }
145 }
146
147 /**
148 * Get the current state of the Bluetooth Headset service.
149 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
150 * object is currently not connected to the Headset service.
151 */
152 public int getState() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800153 if (DBG) log("getState()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800154 if (mService != null) {
155 try {
156 return mService.getState();
157 } catch (RemoteException e) {Log.e(TAG, e.toString());}
158 } else {
159 Log.w(TAG, "Proxy not attached to service");
160 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
161 }
162 return BluetoothHeadset.STATE_ERROR;
163 }
164
165 /**
Nick Pelly2d664882009-08-14 18:33:38 -0700166 * Get the BluetoothDevice for the current headset.
167 * @return current headset, or null if not in connected or connecting
The Android Open Source Project33897762009-03-03 19:31:44 -0800168 * state, or if this proxy object is not connected to the Headset
169 * service.
170 */
Nick Pelly2d664882009-08-14 18:33:38 -0700171 public BluetoothDevice getCurrentHeadset() {
172 if (DBG) log("getCurrentHeadset()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800173 if (mService != null) {
174 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700175 return mService.getCurrentHeadset();
The Android Open Source Project33897762009-03-03 19:31:44 -0800176 } catch (RemoteException e) {Log.e(TAG, e.toString());}
177 } else {
178 Log.w(TAG, "Proxy not attached to service");
179 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
180 }
181 return null;
182 }
183
184 /**
185 * Request to initiate a connection to a headset.
186 * This call does not block. Fails if a headset is already connecting
187 * or connected.
Nick Pelly2d664882009-08-14 18:33:38 -0700188 * Initiates auto-connection if device is null. Tries to connect to all
The Android Open Source Project33897762009-03-03 19:31:44 -0800189 * devices with priority greater than PRIORITY_AUTO in descending order.
Nick Pelly2d664882009-08-14 18:33:38 -0700190 * @param device device to connect to, or null to auto-connect last connected
191 * headset
192 * @return false if there was a problem initiating the connection
193 * procedure, and no further HEADSET_STATE_CHANGED intents
194 * will be expected.
The Android Open Source Project33897762009-03-03 19:31:44 -0800195 */
Nick Pelly2d664882009-08-14 18:33:38 -0700196 public boolean connectHeadset(BluetoothDevice device) {
197 if (DBG) log("connectHeadset(" + device + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800198 if (mService != null) {
199 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700200 if (mService.connectHeadset(device)) {
The Android Open Source Project33897762009-03-03 19:31:44 -0800201 return true;
202 }
203 } catch (RemoteException e) {Log.e(TAG, e.toString());}
204 } else {
205 Log.w(TAG, "Proxy not attached to service");
206 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
207 }
208 return false;
209 }
210
211 /**
212 * Returns true if the specified headset is connected (does not include
213 * connecting). Returns false if not connected, or if this proxy object
214 * if not currently connected to the headset service.
215 */
Nick Pelly2d664882009-08-14 18:33:38 -0700216 public boolean isConnected(BluetoothDevice device) {
217 if (DBG) log("isConnected(" + device + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800218 if (mService != null) {
219 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700220 return mService.isConnected(device);
The Android Open Source Project33897762009-03-03 19:31:44 -0800221 } catch (RemoteException e) {Log.e(TAG, e.toString());}
222 } else {
223 Log.w(TAG, "Proxy not attached to service");
224 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
225 }
226 return false;
227 }
228
229 /**
230 * Disconnects the current headset. Currently this call blocks, it may soon
231 * be made asynchornous. Returns false if this proxy object is
232 * not currently connected to the Headset service.
233 */
234 public boolean disconnectHeadset() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800235 if (DBG) log("disconnectHeadset()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800236 if (mService != null) {
237 try {
238 mService.disconnectHeadset();
239 return true;
240 } catch (RemoteException e) {Log.e(TAG, e.toString());}
241 } else {
242 Log.w(TAG, "Proxy not attached to service");
243 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
244 }
245 return false;
246 }
247
248 /**
249 * Start BT Voice Recognition mode, and set up Bluetooth audio path.
250 * Returns false if there is no headset connected, or if the
251 * connected headset does not support voice recognition, or on
252 * error.
253 */
254 public boolean startVoiceRecognition() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800255 if (DBG) log("startVoiceRecognition()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800256 if (mService != null) {
257 try {
258 return mService.startVoiceRecognition();
259 } catch (RemoteException e) {Log.e(TAG, e.toString());}
260 } else {
261 Log.w(TAG, "Proxy not attached to service");
262 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
263 }
264 return false;
265 }
266
267 /**
268 * Stop BT Voice Recognition mode, and shut down Bluetooth audio path.
269 * Returns false if there is no headset connected, or the connected
270 * headset is not in voice recognition mode, or on error.
271 */
272 public boolean stopVoiceRecognition() {
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800273 if (DBG) log("stopVoiceRecognition()");
The Android Open Source Project33897762009-03-03 19:31:44 -0800274 if (mService != null) {
275 try {
276 return mService.stopVoiceRecognition();
277 } catch (RemoteException e) {Log.e(TAG, e.toString());}
278 } else {
279 Log.w(TAG, "Proxy not attached to service");
280 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
281 }
282 return false;
283 }
284
285 /**
286 * Set priority of headset.
287 * Priority is a non-negative integer. By default paired headsets will have
288 * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
289 * Headsets with priority greater than zero will be auto-connected, and
290 * incoming connections will be accepted (if no other headset is
291 * connected).
292 * Auto-connection occurs at the following events: boot, incoming phone
293 * call, outgoing phone call.
294 * Headsets with priority equal to zero, or that are unpaired, are not
295 * auto-connected.
296 * Incoming connections are ignored regardless of priority if there is
297 * already a headset connected.
Nick Pelly2d664882009-08-14 18:33:38 -0700298 * @param device paired headset
The Android Open Source Project33897762009-03-03 19:31:44 -0800299 * @param priority Integer priority, for example PRIORITY_AUTO or
300 * PRIORITY_NONE
Nick Pelly2d664882009-08-14 18:33:38 -0700301 * @return true if successful, false if there was some error
The Android Open Source Project33897762009-03-03 19:31:44 -0800302 */
Nick Pelly2d664882009-08-14 18:33:38 -0700303 public boolean setPriority(BluetoothDevice device, int priority) {
304 if (DBG) log("setPriority(" + device + ", " + priority + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800305 if (mService != null) {
306 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700307 return mService.setPriority(device, priority);
The Android Open Source Project33897762009-03-03 19:31:44 -0800308 } catch (RemoteException e) {Log.e(TAG, e.toString());}
309 } else {
310 Log.w(TAG, "Proxy not attached to service");
311 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
312 }
313 return false;
314 }
315
316 /**
317 * Get priority of headset.
Nick Pelly2d664882009-08-14 18:33:38 -0700318 * @param device headset
319 * @return non-negative priority, or negative error code on error
The Android Open Source Project33897762009-03-03 19:31:44 -0800320 */
Nick Pelly2d664882009-08-14 18:33:38 -0700321 public int getPriority(BluetoothDevice device) {
322 if (DBG) log("getPriority(" + device + ")");
The Android Open Source Project33897762009-03-03 19:31:44 -0800323 if (mService != null) {
324 try {
Nick Pelly2d664882009-08-14 18:33:38 -0700325 return mService.getPriority(device);
The Android Open Source Project33897762009-03-03 19:31:44 -0800326 } catch (RemoteException e) {Log.e(TAG, e.toString());}
327 } else {
328 Log.w(TAG, "Proxy not attached to service");
329 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
330 }
331 return -1;
332 }
333
334 /**
Nick Pelly312cd3a2009-06-19 10:08:09 -0700335 * Get battery usage hint for Bluetooth Headset service.
336 * This is a monotonically increasing integer. Wraps to 0 at
337 * Integer.MAX_INT, and at boot.
338 * Current implementation returns the number of AT commands handled since
339 * boot. This is a good indicator for spammy headset/handsfree units that
340 * can keep the device awake by polling for cellular status updates. As a
341 * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
342 * @return monotonically increasing battery usage hint, or a negative error
343 * code on error
344 * @hide
345 */
346 public int getBatteryUsageHint() {
347 if (DBG) log("getBatteryUsageHint()");
348 if (mService != null) {
349 try {
350 return mService.getBatteryUsageHint();
351 } catch (RemoteException e) {Log.e(TAG, e.toString());}
352 } else {
353 Log.w(TAG, "Proxy not attached to service");
354 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
355 }
356 return -1;
357 }
358
359 /**
The Android Open Source Project33897762009-03-03 19:31:44 -0800360 * Check class bits for possible HSP or HFP support.
361 * This is a simple heuristic that tries to guess if a device with the
362 * given class bits might support HSP or HFP. It is not accurate for all
363 * devices. It tries to err on the side of false positives.
364 * @return True if this device might support HSP or HFP.
365 */
366 public static boolean doesClassMatch(int btClass) {
367 // The render service class is required by the spec for HFP, so is a
368 // pretty good signal
369 if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
370 return true;
371 }
372 // Just in case they forgot the render service class
373 switch (BluetoothClass.Device.getDevice(btClass)) {
374 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
375 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
376 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
377 return true;
378 default:
379 return false;
380 }
381 }
382
383 private ServiceConnection mConnection = new ServiceConnection() {
384 public void onServiceConnected(ComponentName className, IBinder service) {
385 if (DBG) Log.d(TAG, "Proxy object connected");
386 mService = IBluetoothHeadset.Stub.asInterface(service);
387 if (mServiceListener != null) {
388 mServiceListener.onServiceConnected();
389 }
390 }
391 public void onServiceDisconnected(ComponentName className) {
392 if (DBG) Log.d(TAG, "Proxy object disconnected");
393 mService = null;
394 if (mServiceListener != null) {
395 mServiceListener.onServiceDisconnected();
396 }
397 }
398 };
The Android Open Source Project0047a0f2009-03-05 20:00:43 -0800399
400 private static void log(String msg) {
401 Log.d(TAG, msg);
402 }
The Android Open Source Project33897762009-03-03 19:31:44 -0800403}