• 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                                     doBind();
133                                 }
134                             } catch (Exception re) {
135                                 Log.e(TAG,"",re);
136                             }
137                         }
138                     }
139                 }
140         };
141 
142     /**
143      * Create a BluetoothPbap proxy object.
144      */
BluetoothPbap(Context context, ServiceListener l)145     public BluetoothPbap(Context context, ServiceListener l) {
146         mContext = context;
147         mServiceListener = l;
148         mAdapter = BluetoothAdapter.getDefaultAdapter();
149         IBluetoothManager mgr = mAdapter.getBluetoothManager();
150         if (mgr != null) {
151             try {
152                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
153             } catch (RemoteException e) {
154                 Log.e(TAG,"",e);
155             }
156         }
157         doBind();
158     }
159 
doBind()160     boolean doBind() {
161         Intent intent = new Intent(IBluetoothPbap.class.getName());
162         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
163         intent.setComponent(comp);
164         if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
165             Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
166             return false;
167         }
168         return true;
169     }
170 
finalize()171     protected void finalize() throws Throwable {
172         try {
173             close();
174         } finally {
175             super.finalize();
176         }
177     }
178 
179     /**
180      * Close the connection to the backing service.
181      * Other public functions of BluetoothPbap will return default error
182      * results once close() has been called. Multiple invocations of close()
183      * are ok.
184      */
close()185     public synchronized void close() {
186         IBluetoothManager mgr = mAdapter.getBluetoothManager();
187         if (mgr != null) {
188             try {
189                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
190             } catch (Exception e) {
191                 Log.e(TAG,"",e);
192             }
193         }
194 
195         synchronized (mConnection) {
196             if (mService != null) {
197                 try {
198                     mService = null;
199                     mContext.unbindService(mConnection);
200                 } catch (Exception re) {
201                     Log.e(TAG,"",re);
202                 }
203             }
204         }
205         mServiceListener = null;
206     }
207 
208     /**
209      * Get the current state of the BluetoothPbap service.
210      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
211      *         object is currently not connected to the Pbap service.
212      */
getState()213     public int getState() {
214         if (VDBG) log("getState()");
215         if (mService != null) {
216             try {
217                 return mService.getState();
218             } catch (RemoteException e) {Log.e(TAG, e.toString());}
219         } else {
220             Log.w(TAG, "Proxy not attached to service");
221             if (DBG) log(Log.getStackTraceString(new Throwable()));
222         }
223         return BluetoothPbap.STATE_ERROR;
224     }
225 
226     /**
227      * Get the currently connected remote Bluetooth device (PCE).
228      * @return The remote Bluetooth device, or null if not in connected or
229      *         connecting state, or if this proxy object is not connected to
230      *         the Pbap service.
231      */
getClient()232     public BluetoothDevice getClient() {
233         if (VDBG) log("getClient()");
234         if (mService != null) {
235             try {
236                 return mService.getClient();
237             } catch (RemoteException e) {Log.e(TAG, e.toString());}
238         } else {
239             Log.w(TAG, "Proxy not attached to service");
240             if (DBG) log(Log.getStackTraceString(new Throwable()));
241         }
242         return null;
243     }
244 
245     /**
246      * Returns true if the specified Bluetooth device is connected (does not
247      * include connecting). Returns false if not connected, or if this proxy
248      * object is not currently connected to the Pbap service.
249      */
isConnected(BluetoothDevice device)250     public boolean isConnected(BluetoothDevice device) {
251         if (VDBG) log("isConnected(" + device + ")");
252         if (mService != null) {
253             try {
254                 return mService.isConnected(device);
255             } catch (RemoteException e) {Log.e(TAG, e.toString());}
256         } else {
257             Log.w(TAG, "Proxy not attached to service");
258             if (DBG) log(Log.getStackTraceString(new Throwable()));
259         }
260         return false;
261     }
262 
263     /**
264      * Disconnects the current Pbap client (PCE). Currently this call blocks,
265      * it may soon be made asynchronous. Returns false if this proxy object is
266      * not currently connected to the Pbap service.
267      */
disconnect()268     public boolean disconnect() {
269         if (DBG) log("disconnect()");
270         if (mService != null) {
271             try {
272                 mService.disconnect();
273                 return true;
274             } catch (RemoteException e) {Log.e(TAG, e.toString());}
275         } else {
276             Log.w(TAG, "Proxy not attached to service");
277             if (DBG) log(Log.getStackTraceString(new Throwable()));
278         }
279         return false;
280     }
281 
282     /**
283      * Check class bits for possible PBAP support.
284      * This is a simple heuristic that tries to guess if a device with the
285      * given class bits might support PBAP. It is not accurate for all
286      * devices. It tries to err on the side of false positives.
287      * @return True if this device might support PBAP.
288      */
doesClassMatchSink(BluetoothClass btClass)289     public static boolean doesClassMatchSink(BluetoothClass btClass) {
290         // TODO optimize the rule
291         switch (btClass.getDeviceClass()) {
292         case BluetoothClass.Device.COMPUTER_DESKTOP:
293         case BluetoothClass.Device.COMPUTER_LAPTOP:
294         case BluetoothClass.Device.COMPUTER_SERVER:
295         case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
296             return true;
297         default:
298             return false;
299         }
300     }
301 
302     private final ServiceConnection mConnection = new ServiceConnection() {
303         public void onServiceConnected(ComponentName className, IBinder service) {
304             if (DBG) log("Proxy object connected");
305             mService = IBluetoothPbap.Stub.asInterface(service);
306             if (mServiceListener != null) {
307                 mServiceListener.onServiceConnected(BluetoothPbap.this);
308             }
309         }
310         public void onServiceDisconnected(ComponentName className) {
311             if (DBG) log("Proxy object disconnected");
312             mService = null;
313             if (mServiceListener != null) {
314                 mServiceListener.onServiceDisconnected();
315             }
316         }
317     };
318 
log(String msg)319     private static void log(String msg) {
320         Log.d(TAG, msg);
321     }
322 }
323