• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.bluetooth;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.os.RemoteException;
24 import android.os.IBinder;
25 import android.os.ServiceManager;
26 import android.util.Log;
27 
28 /**
29  * The Android Bluetooth API is not finalized, and *will* change. Use at your
30  * own risk.
31  *
32  * Public API for controlling the Bluetooth Pbap Service. This includes
33  * Bluetooth Phone book Access profile.
34  * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
35  * Service via IPC.
36  *
37  * Creating a BluetoothPbap object will create a binding with the
38  * BluetoothPbap service. Users of this object should call close() when they
39  * are finished with the BluetoothPbap, so that this proxy object can unbind
40  * from the service.
41  *
42  * This BluetoothPbap object is not immediately bound to the
43  * BluetoothPbap service. Use the ServiceListener interface to obtain a
44  * notification when it is bound, this is especially important if you wish to
45  * immediately call methods on BluetoothPbap after construction.
46  *
47  * Android only supports one connected Bluetooth Pce at a time.
48  *
49  * @hide
50  */
51 public class BluetoothPbap {
52 
53     private static final String TAG = "BluetoothPbap";
54     private static final boolean DBG = true;
55     private static final boolean VDBG = false;
56 
57     /** int extra for PBAP_STATE_CHANGED_ACTION */
58     public static final String PBAP_STATE =
59         "android.bluetooth.pbap.intent.PBAP_STATE";
60     /** int extra for PBAP_STATE_CHANGED_ACTION */
61     public static final String PBAP_PREVIOUS_STATE =
62         "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
63 
64     /** Indicates the state of a pbap connection state has changed.
65      *  This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
66      *  BluetoothIntent.ADDRESS extras.
67      */
68     public static final String PBAP_STATE_CHANGED_ACTION =
69         "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
70 
71     private IBluetoothPbap mService;
72     private final Context mContext;
73     private ServiceListener mServiceListener;
74     private BluetoothAdapter mAdapter;
75 
76     /** There was an error trying to obtain the state */
77     public static final int STATE_ERROR        = -1;
78     /** No client currently connected */
79     public static final int STATE_DISCONNECTED = 0;
80     /** Connection attempt in progress */
81     public static final int STATE_CONNECTING   = 1;
82     /** Client 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     /**
91      * An interface for notifying Bluetooth PCE IPC clients when they have
92      * been connected to the BluetoothPbap service.
93      */
94     public interface ServiceListener {
95         /**
96          * Called to notify the client when this proxy object has been
97          * connected to the BluetoothPbap service. Clients must wait for
98          * this callback before making IPC calls on the BluetoothPbap
99          * service.
100          */
onServiceConnected(BluetoothPbap proxy)101         public void onServiceConnected(BluetoothPbap proxy);
102 
103         /**
104          * Called to notify the client that this proxy object has been
105          * disconnected from the BluetoothPbap service. Clients must not
106          * make IPC calls on the BluetoothPbap service after this callback.
107          * This callback will currently only occur if the application hosting
108          * the BluetoothPbap service, but may be called more often in future.
109          */
onServiceDisconnected()110         public void onServiceDisconnected();
111     }
112 
113     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
114             new IBluetoothStateChangeCallback.Stub() {
115                 public void onBluetoothStateChange(boolean up) {
116                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
117                     if (!up) {
118                         if (VDBG) Log.d(TAG,"Unbinding service...");
119                         synchronized (mConnection) {
120                             try {
121                                 mService = null;
122                                 mContext.unbindService(mConnection);
123                             } catch (Exception re) {
124                                 Log.e(TAG,"",re);
125                             }
126                         }
127                     } else {
128                         synchronized (mConnection) {
129                             try {
130                                 if (mService == null) {
131                                     if (VDBG) Log.d(TAG,"Binding service...");
132                                     if (!mContext.bindService(
133                                                         new Intent(IBluetoothPbap.class.getName()),
134                                                         mConnection, 0)) {
135                                         Log.e(TAG, "Could not bind to Bluetooth PBAP Service");
136                                     }
137                                 }
138                             } catch (Exception re) {
139                                 Log.e(TAG,"",re);
140                             }
141                         }
142                     }
143                 }
144         };
145 
146     /**
147      * Create a BluetoothPbap proxy object.
148      */
BluetoothPbap(Context context, ServiceListener l)149     public BluetoothPbap(Context context, ServiceListener l) {
150         mContext = context;
151         mServiceListener = l;
152         mAdapter = BluetoothAdapter.getDefaultAdapter();
153         IBluetoothManager mgr = mAdapter.getBluetoothManager();
154         if (mgr != null) {
155             try {
156                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
157             } catch (RemoteException e) {
158                 Log.e(TAG,"",e);
159             }
160         }
161         if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) {
162             Log.e(TAG, "Could not bind to Bluetooth Pbap Service");
163         }
164     }
165 
finalize()166     protected void finalize() throws Throwable {
167         try {
168             close();
169         } finally {
170             super.finalize();
171         }
172     }
173 
174     /**
175      * Close the connection to the backing service.
176      * Other public functions of BluetoothPbap will return default error
177      * results once close() has been called. Multiple invocations of close()
178      * are ok.
179      */
close()180     public synchronized void close() {
181         IBluetoothManager mgr = mAdapter.getBluetoothManager();
182         if (mgr != null) {
183             try {
184                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
185             } catch (Exception e) {
186                 Log.e(TAG,"",e);
187             }
188         }
189 
190         synchronized (mConnection) {
191             if (mService != null) {
192                 try {
193                     mService = null;
194                     mContext.unbindService(mConnection);
195                     mConnection = null;
196                 } catch (Exception re) {
197                     Log.e(TAG,"",re);
198                 }
199             }
200         }
201         mServiceListener = null;
202     }
203 
204     /**
205      * Get the current state of the BluetoothPbap service.
206      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
207      *         object is currently not connected to the Pbap service.
208      */
getState()209     public int getState() {
210         if (VDBG) log("getState()");
211         if (mService != null) {
212             try {
213                 return mService.getState();
214             } catch (RemoteException e) {Log.e(TAG, e.toString());}
215         } else {
216             Log.w(TAG, "Proxy not attached to service");
217             if (DBG) log(Log.getStackTraceString(new Throwable()));
218         }
219         return BluetoothPbap.STATE_ERROR;
220     }
221 
222     /**
223      * Get the currently connected remote Bluetooth device (PCE).
224      * @return The remote Bluetooth device, or null if not in connected or
225      *         connecting state, or if this proxy object is not connected to
226      *         the Pbap service.
227      */
getClient()228     public BluetoothDevice getClient() {
229         if (VDBG) log("getClient()");
230         if (mService != null) {
231             try {
232                 return mService.getClient();
233             } catch (RemoteException e) {Log.e(TAG, e.toString());}
234         } else {
235             Log.w(TAG, "Proxy not attached to service");
236             if (DBG) log(Log.getStackTraceString(new Throwable()));
237         }
238         return null;
239     }
240 
241     /**
242      * Returns true if the specified Bluetooth device is connected (does not
243      * include connecting). Returns false if not connected, or if this proxy
244      * object is not currently connected to the Pbap service.
245      */
isConnected(BluetoothDevice device)246     public boolean isConnected(BluetoothDevice device) {
247         if (VDBG) log("isConnected(" + device + ")");
248         if (mService != null) {
249             try {
250                 return mService.isConnected(device);
251             } catch (RemoteException e) {Log.e(TAG, e.toString());}
252         } else {
253             Log.w(TAG, "Proxy not attached to service");
254             if (DBG) log(Log.getStackTraceString(new Throwable()));
255         }
256         return false;
257     }
258 
259     /**
260      * Disconnects the current Pbap client (PCE). Currently this call blocks,
261      * it may soon be made asynchronous. Returns false if this proxy object is
262      * not currently connected to the Pbap service.
263      */
disconnect()264     public boolean disconnect() {
265         if (DBG) log("disconnect()");
266         if (mService != null) {
267             try {
268                 mService.disconnect();
269                 return true;
270             } catch (RemoteException e) {Log.e(TAG, e.toString());}
271         } else {
272             Log.w(TAG, "Proxy not attached to service");
273             if (DBG) log(Log.getStackTraceString(new Throwable()));
274         }
275         return false;
276     }
277 
278     /**
279      * Check class bits for possible PBAP support.
280      * This is a simple heuristic that tries to guess if a device with the
281      * given class bits might support PBAP. It is not accurate for all
282      * devices. It tries to err on the side of false positives.
283      * @return True if this device might support PBAP.
284      */
doesClassMatchSink(BluetoothClass btClass)285     public static boolean doesClassMatchSink(BluetoothClass btClass) {
286         // TODO optimize the rule
287         switch (btClass.getDeviceClass()) {
288         case BluetoothClass.Device.COMPUTER_DESKTOP:
289         case BluetoothClass.Device.COMPUTER_LAPTOP:
290         case BluetoothClass.Device.COMPUTER_SERVER:
291         case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
292             return true;
293         default:
294             return false;
295         }
296     }
297 
298     private ServiceConnection mConnection = new ServiceConnection() {
299         public void onServiceConnected(ComponentName className, IBinder service) {
300             if (DBG) log("Proxy object connected");
301             mService = IBluetoothPbap.Stub.asInterface(service);
302             if (mServiceListener != null) {
303                 mServiceListener.onServiceConnected(BluetoothPbap.this);
304             }
305         }
306         public void onServiceDisconnected(ComponentName className) {
307             if (DBG) log("Proxy object disconnected");
308             mService = null;
309             if (mServiceListener != null) {
310                 mServiceListener.onServiceDisconnected();
311             }
312         }
313     };
314 
log(String msg)315     private static void log(String msg) {
316         Log.d(TAG, msg);
317     }
318 }
319