BluetoothPbap.java revision 3f41673265dcaaef058703311c5481e8a51fd8be
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.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import 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.os.ServiceManager;
28import android.util.Log;
29
30/**
31 * The Android Bluetooth API is not finalized, and *will* change. Use at your
32 * own risk.
33 *
34 * Public API for controlling the Bluetooth Pbap Service. This includes
35 * Bluetooth Phone book Access profile.
36 * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
37 * Service via IPC.
38 *
39 * Creating a BluetoothPbap object will create a binding with the
40 * BluetoothPbap service. Users of this object should call close() when they
41 * are finished with the BluetoothPbap, so that this proxy object can unbind
42 * from the service.
43 *
44 * This BluetoothPbap object is not immediately bound to the
45 * BluetoothPbap service. Use the ServiceListener interface to obtain a
46 * notification when it is bound, this is especially important if you wish to
47 * immediately call methods on BluetoothPbap after construction.
48 *
49 * Android only supports one connected Bluetooth Pce at a time.
50 *
51 * @hide
52 */
53public class BluetoothPbap {
54
55    private static final String TAG = "BluetoothPbap";
56    private static final boolean DBG = false;
57
58    /** int extra for PBAP_STATE_CHANGED_ACTION */
59    public static final String PBAP_STATE =
60        "android.bluetooth.pbap.intent.PBAP_STATE";
61    /** int extra for PBAP_STATE_CHANGED_ACTION */
62    public static final String PBAP_PREVIOUS_STATE =
63        "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
64
65    /** Indicates the state of an pbap connection state has changed.
66     *  This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
67     *  BluetoothIntent.ADDRESS extras.
68     */
69    public static final String PBAP_STATE_CHANGED_ACTION =
70        "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
71
72    private IBluetoothPbap mService;
73    private final Context mContext;
74    private final ServiceListener mServiceListener;
75
76    /** There was an error trying to obtain the state */
77    public static final int STATE_ERROR        = -1;
78    /** No headset currently connected */
79    public static final int STATE_DISCONNECTED = 0;
80    /** Connection attempt in progress */
81    public static final int STATE_CONNECTING   = 1;
82    /** A headset is currently connected */
83    public static final int STATE_CONNECTED    = 2;
84
85    public static final int RESULT_FAILURE = 0;
86    public static final int RESULT_SUCCESS = 1;
87    /** Connection canceled before completion. */
88    public static final int RESULT_CANCELED = 2;
89
90    public static final int PRIORITY_AUTO = 100;
91    public static final int PRIORITY_OFF = 0;
92    /**
93     * An interface for notifying Bluetooth PCE IPC clients when they have
94     * been connected to the BluetoothHeadset service.
95     */
96    public interface ServiceListener {
97        /**
98         * Called to notify the client when this proxy object has been
99         * connected to the BluetoothPbap service. Clients must wait for
100         * this callback before making IPC calls on the BluetoothPbap
101         * service.
102         */
103        public void onServiceConnected();
104
105        /**
106         * Called to notify the client that this proxy object has been
107         * disconnected from the BluetoothPbap service. Clients must not
108         * make IPC calls on the BluetoothPbap service after this callback.
109         * This callback will currently only occur if the application hosting
110         * the BluetoothPbap service, but may be called more often in future.
111         */
112        public void onServiceDisconnected();
113    }
114
115    /**
116     * Create a BluetoothHeadset proxy object.
117     */
118    public BluetoothPbap(Context context, ServiceListener l) {
119        mContext = context;
120        mServiceListener = l;
121        if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) {
122            Log.e(TAG, "Could not bind to Bluetooth Pbap Service");
123        }
124    }
125
126    protected void finalize() throws Throwable {
127        try {
128            close();
129        } finally {
130            super.finalize();
131        }
132    }
133
134    /**
135     * Close the connection to the backing service.
136     * Other public functions of BluetoothHeadset will return default error
137     * results once close() has been called. Multiple invocations of close()
138     * are ok.
139     */
140    public synchronized void close() {
141        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() {
153        if (DBG) log("getState()");
154        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(Log.getStackTraceString(new Throwable()));
161        }
162        return BluetoothHeadset.STATE_ERROR;
163    }
164
165    /**
166     * Get the Bluetooth address of the current headset.
167     * @return The Bluetooth address, or null if not in connected or connecting
168     *         state, or if this proxy object is not connected to the Headset
169     *         service.
170     */
171    public String getPceAddress() {
172        if (DBG) log("getPceAddress()");
173        if (mService != null) {
174            try {
175                return mService.getPceAddress();
176            } catch (RemoteException e) {Log.e(TAG, e.toString());}
177        } else {
178            Log.w(TAG, "Proxy not attached to service");
179            if (DBG) log(Log.getStackTraceString(new Throwable()));
180        }
181        return null;
182    }
183
184    /**
185     * Returns true if the specified headset is connected (does not include
186     * connecting). Returns false if not connected, or if this proxy object
187     * if not currently connected to the headset service.
188     */
189    public boolean isConnected(String address) {
190        if (DBG) log("isConnected(" + address + ")");
191        if (mService != null) {
192            try {
193                return mService.isConnected(address);
194            } catch (RemoteException e) {Log.e(TAG, e.toString());}
195        } else {
196            Log.w(TAG, "Proxy not attached to service");
197            if (DBG) log(Log.getStackTraceString(new Throwable()));
198        }
199        return false;
200    }
201
202    /**
203     * Disconnects the current headset. Currently this call blocks, it may soon
204     * be made asynchornous. Returns false if this proxy object is
205     * not currently connected to the Headset service.
206     */
207    public boolean disconnectPce() {
208        if (DBG) log("disconnectPce()");
209        if (mService != null) {
210            try {
211                mService.disconnectPce();
212                return true;
213            } catch (RemoteException e) {Log.e(TAG, e.toString());}
214        } else {
215            Log.w(TAG, "Proxy not attached to service");
216            if (DBG) log(Log.getStackTraceString(new Throwable()));
217        }
218        return false;
219    }
220
221    /**
222     * Check class bits for possible PBAP support.
223     * This is a simple heuristic that tries to guess if a device with the
224     * given class bits might support PBAP. It is not accurate for all
225     * devices. It tries to err on the side of false positives.
226     * @return True if this device might support PBAP.
227     */
228    public static boolean doesClassMatchSink(int btClass) {
229        // TODO optimize the rule
230        switch (BluetoothClass.Device.getDevice(btClass)) {
231        case BluetoothClass.Device.COMPUTER_DESKTOP:
232        case BluetoothClass.Device.COMPUTER_LAPTOP:
233        case BluetoothClass.Device.COMPUTER_SERVER:
234        case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
235            return true;
236        default:
237            return false;
238        }
239    }
240
241    private ServiceConnection mConnection = new ServiceConnection() {
242        public void onServiceConnected(ComponentName className, IBinder service) {
243            if (DBG) log("Proxy object connected");
244            mService = IBluetoothPbap.Stub.asInterface(service);
245            if (mServiceListener != null) {
246                mServiceListener.onServiceConnected();
247            }
248        }
249        public void onServiceDisconnected(ComponentName className) {
250            if (DBG) log("Proxy object disconnected");
251            mService = null;
252            if (mServiceListener != null) {
253                mServiceListener.onServiceDisconnected();
254            }
255        }
256    };
257
258    private static void log(String msg) {
259        Log.d(TAG, msg);
260    }
261}
262