BluetoothHeadset.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/*
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 */
52public class BluetoothHeadset {
53
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    private ConnectHeadsetCallback mConnectHeadsetCallback;
61
62    /** There was an error trying to obtain the state */
63    public static final int STATE_ERROR        = -1;
64    /** No headset currently connected */
65    public static final int STATE_DISCONNECTED = 0;
66    /** Connection attempt in progress */
67    public static final int STATE_CONNECTING   = 1;
68    /** A headset is currently connected */
69    public static final int STATE_CONNECTED    = 2;
70
71    public static final int RESULT_FAILURE = 0;
72    public static final int RESULT_SUCCESS = 1;
73    /** Connection cancelled before completetion. */
74    public static final int RESULT_CANCELLED = 2;
75
76    /**
77     * An interface for notifying BluetoothHeadset IPC clients when they have
78     * been connected to the BluetoothHeadset service.
79     */
80    public interface ServiceListener {
81        /**
82         * Called to notify the client when this proxy object has been
83         * connected to the BluetoothHeadset service. Clients must wait for
84         * this callback before making IPC calls on the BluetoothHeadset
85         * service.
86         */
87        public void onServiceConnected();
88
89        /**
90         * Called to notify the client that this proxy object has been
91         * disconnected from the BluetoothHeadset service. Clients must not
92         * make IPC calls on the BluetoothHeadset service after this callback.
93         * This callback will currently only occur if the application hosting
94         * the BluetoothHeadset service, but may be called more often in future.
95         */
96        public void onServiceDisconnected();
97    }
98
99    /**
100     * Interface for connectHeadset() callback.
101     * This callback can occur in the Binder thread.
102     */
103    public interface ConnectHeadsetCallback {
104        public void onConnectHeadsetResult(String address, int resultCode);
105    }
106
107    /**
108     * Create a BluetoothHeadset proxy object.
109     */
110    public BluetoothHeadset(Context context, ServiceListener l) {
111        mContext = context;
112        mServiceListener = l;
113        if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
114            Log.e(TAG, "Could not bind to Bluetooth Headset Service");
115        }
116    }
117
118    protected void finalize() throws Throwable {
119        try {
120            close();
121        } finally {
122            super.finalize();
123        }
124    }
125
126    /**
127     * Close the connection to the backing service.
128     * Other public functions of BluetoothHeadset will return default error
129     * results once close() has been called. Multiple invocations of close()
130     * are ok.
131     */
132    public synchronized void close() {
133        if (mConnection != null) {
134            mContext.unbindService(mConnection);
135            mConnection = null;
136        }
137    }
138
139    /**
140     * Get the current state of the Bluetooth Headset service.
141     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
142     *         object is currently not connected to the Headset service.
143     */
144    public int getState() {
145        if (mService != null) {
146            try {
147                return mService.getState();
148            } catch (RemoteException e) {Log.e(TAG, e.toString());}
149        } else {
150            Log.w(TAG, "Proxy not attached to service");
151            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
152        }
153        return BluetoothHeadset.STATE_ERROR;
154    }
155
156    /**
157     * Get the Bluetooth address of the current headset.
158     * @return The Bluetooth address, or null if not in connected or connecting
159     *         state, or if this proxy object is not connected to the Headset
160     *         service.
161     */
162    public String getHeadsetAddress() {
163        if (mService != null) {
164            try {
165                return mService.getHeadsetAddress();
166            } catch (RemoteException e) {Log.e(TAG, e.toString());}
167        } else {
168            Log.w(TAG, "Proxy not attached to service");
169            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
170        }
171        return null;
172    }
173
174    /**
175     * Request to initiate a connection to a headset.
176     * This call does not block. Fails if a headset is already connecting
177     * or connected.
178     * Will connect to the last connected headset if address is null.
179     * onConnectHeadsetResult() of your ConnectHeadsetCallback will be called
180     * on completition.
181     * @param address The Bluetooth Address to connect to, or null to connect
182     *                to the last connected headset.
183     * @param callback Callback on result. Not called if false is returned. Can
184     *                be null.
185     *                to the last connected headset.
186     * @return        False if there was a problem initiating the connection
187     *                procedure, and your callback will not be used. True if
188     *                the connection procedure was initiated, in which case
189     *                your callback is guarenteed to be called.
190     */
191    public boolean connectHeadset(String address, ConnectHeadsetCallback callback) {
192        if (mService != null) {
193            try {
194                if (mService.connectHeadset(address, mHeadsetCallback)) {
195                    mConnectHeadsetCallback = callback;
196                    return true;
197                }
198            } catch (RemoteException e) {Log.e(TAG, e.toString());}
199        } else {
200            Log.w(TAG, "Proxy not attached to service");
201            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
202        }
203        return false;
204    }
205
206    /**
207     * Returns true if the specified headset is connected (does not include
208     * connecting). Returns false if not connected, or if this proxy object
209     * if not currently connected to the headset service.
210     */
211    public boolean isConnected(String address) {
212        if (mService != null) {
213            try {
214                return mService.isConnected(address);
215            } catch (RemoteException e) {Log.e(TAG, e.toString());}
216        } else {
217            Log.w(TAG, "Proxy not attached to service");
218            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
219        }
220        return false;
221    }
222
223    /**
224     * Disconnects the current headset. Currently this call blocks, it may soon
225     * be made asynchornous. Returns false if this proxy object is
226     * not currently connected to the Headset service.
227     */
228    public boolean disconnectHeadset() {
229        if (mService != null) {
230            try {
231                mService.disconnectHeadset();
232                return true;
233            } catch (RemoteException e) {Log.e(TAG, e.toString());}
234        } else {
235            Log.w(TAG, "Proxy not attached to service");
236            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
237        }
238        return false;
239    }
240
241    /**
242     * Start BT Voice Recognition mode, and set up Bluetooth audio path.
243     * Returns false if there is no headset connected, or if the
244     * connected headset does not support voice recognition, or on
245     * error.
246     */
247    public boolean startVoiceRecognition() {
248        if (mService != null) {
249            try {
250                return mService.startVoiceRecognition();
251            } catch (RemoteException e) {Log.e(TAG, e.toString());}
252        } else {
253            Log.w(TAG, "Proxy not attached to service");
254            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
255        }
256        return false;
257    }
258
259    /**
260     * Stop BT Voice Recognition mode, and shut down Bluetooth audio path.
261     * Returns false if there is no headset connected, or the connected
262     * headset is not in voice recognition mode, or on error.
263     */
264    public boolean stopVoiceRecognition() {
265        if (mService != null) {
266            try {
267                return mService.stopVoiceRecognition();
268            } catch (RemoteException e) {Log.e(TAG, e.toString());}
269        } else {
270            Log.w(TAG, "Proxy not attached to service");
271            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
272        }
273        return false;
274    }
275
276    private ServiceConnection mConnection = new ServiceConnection() {
277        public void onServiceConnected(ComponentName className, IBinder service) {
278            if (DBG) Log.d(TAG, "Proxy object connected");
279            mService = IBluetoothHeadset.Stub.asInterface(service);
280            if (mServiceListener != null) {
281                mServiceListener.onServiceConnected();
282            }
283        }
284        public void onServiceDisconnected(ComponentName className) {
285            if (DBG) Log.d(TAG, "Proxy object disconnected");
286            mService = null;
287            if (mServiceListener != null) {
288                mServiceListener.onServiceDisconnected();
289            }
290        }
291    };
292
293    private IBluetoothHeadsetCallback mHeadsetCallback = new IBluetoothHeadsetCallback.Stub() {
294        public void onConnectHeadsetResult(String address, int resultCode) {
295            if (mConnectHeadsetCallback != null) {
296                mConnectHeadsetCallback.onConnectHeadsetResult(address, resultCode);
297            }
298        }
299    };
300}
301