• 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.annotation.SdkConstant;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.os.IBinder;
25 import android.os.RemoteException;
26 import android.util.Log;
27 
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 
32 /**
33  * The Android Bluetooth API is not finalized, and *will* change. Use at your
34  * own risk.
35  *
36  * Public API for controlling the Bluetooth Pbap Service. This includes
37  * Bluetooth Phone book Access profile.
38  * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
39  * Service via IPC.
40  *
41  * Creating a BluetoothPbap object will create a binding with the
42  * BluetoothPbap service. Users of this object should call close() when they
43  * are finished with the BluetoothPbap, so that this proxy object can unbind
44  * from the service.
45  *
46  * This BluetoothPbap object is not immediately bound to the
47  * BluetoothPbap service. Use the ServiceListener interface to obtain a
48  * notification when it is bound, this is especially important if you wish to
49  * immediately call methods on BluetoothPbap after construction.
50  *
51  * Android only supports one connected Bluetooth Pce at a time.
52  *
53  * @hide
54  */
55 public class BluetoothPbap implements BluetoothProfile {
56 
57     private static final String TAG = "BluetoothPbap";
58     private static final boolean DBG = false;
59 
60     /**
61      * Intent used to broadcast the change in connection state of the PBAP
62      * profile.
63      *
64      * <p>This intent will have 3 extras:
65      * <ul>
66      * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
67      * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
68      * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
69      * </ul>
70      * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}
71      *  can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
72      *  {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
73      *  {@link BluetoothProfile#STATE_DISCONNECTING}.
74      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
75      * receive.
76      */
77     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
78     public static final String ACTION_CONNECTION_STATE_CHANGED =
79             "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
80 
81     private volatile IBluetoothPbap mService;
82     private final Context mContext;
83     private ServiceListener mServiceListener;
84     private BluetoothAdapter mAdapter;
85 
86     public static final int RESULT_FAILURE = 0;
87     public static final int RESULT_SUCCESS = 1;
88     /** Connection canceled before completion. */
89     public static final int RESULT_CANCELED = 2;
90 
91     /**
92      * An interface for notifying Bluetooth PCE IPC clients when they have
93      * been connected to the BluetoothPbap service.
94      */
95     public interface ServiceListener {
96         /**
97          * Called to notify the client when this proxy object has been
98          * connected to the BluetoothPbap service. Clients must wait for
99          * this callback before making IPC calls on the BluetoothPbap
100          * service.
101          */
onServiceConnected(BluetoothPbap proxy)102         public void onServiceConnected(BluetoothPbap proxy);
103 
104         /**
105          * Called to notify the client that this proxy object has been
106          * disconnected from the BluetoothPbap service. Clients must not
107          * make IPC calls on the BluetoothPbap service after this callback.
108          * This callback will currently only occur if the application hosting
109          * the BluetoothPbap service, but may be called more often in future.
110          */
onServiceDisconnected()111         public void onServiceDisconnected();
112     }
113 
114     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
115             new IBluetoothStateChangeCallback.Stub() {
116                 public void onBluetoothStateChange(boolean up) {
117                     log("onBluetoothStateChange: up=" + up);
118                     if (!up) {
119                         log("Unbinding service...");
120                         synchronized (mConnection) {
121                             try {
122                                 mService = null;
123                                 mContext.unbindService(mConnection);
124                             } catch (Exception re) {
125                                 Log.e(TAG, "", re);
126                             }
127                         }
128                     } else {
129                         synchronized (mConnection) {
130                             try {
131                                 if (mService == null) {
132                                     log("Binding service...");
133                                     doBind();
134                                 }
135                             } catch (Exception re) {
136                                 Log.e(TAG, "", re);
137                             }
138                         }
139                     }
140                 }
141             };
142 
143     /**
144      * Create a BluetoothPbap proxy object.
145      */
BluetoothPbap(Context context, ServiceListener l)146     public BluetoothPbap(Context context, ServiceListener l) {
147         mContext = context;
148         mServiceListener = l;
149         mAdapter = BluetoothAdapter.getDefaultAdapter();
150         IBluetoothManager mgr = mAdapter.getBluetoothManager();
151         if (mgr != null) {
152             try {
153                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
154             } catch (RemoteException e) {
155                 Log.e(TAG, "", e);
156             }
157         }
158         doBind();
159     }
160 
doBind()161     boolean doBind() {
162         Intent intent = new Intent(IBluetoothPbap.class.getName());
163         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
164         intent.setComponent(comp);
165         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
166                 mContext.getUser())) {
167             Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
168             return false;
169         }
170         return true;
171     }
172 
finalize()173     protected void finalize() throws Throwable {
174         try {
175             close();
176         } finally {
177             super.finalize();
178         }
179     }
180 
181     /**
182      * Close the connection to the backing service.
183      * Other public functions of BluetoothPbap will return default error
184      * results once close() has been called. Multiple invocations of close()
185      * are ok.
186      */
close()187     public synchronized void close() {
188         IBluetoothManager mgr = mAdapter.getBluetoothManager();
189         if (mgr != null) {
190             try {
191                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
192             } catch (Exception e) {
193                 Log.e(TAG, "", e);
194             }
195         }
196 
197         synchronized (mConnection) {
198             if (mService != null) {
199                 try {
200                     mService = null;
201                     mContext.unbindService(mConnection);
202                 } catch (Exception re) {
203                     Log.e(TAG, "", re);
204                 }
205             }
206         }
207         mServiceListener = null;
208     }
209 
210     /**
211      * {@inheritDoc}
212      */
213     @Override
getConnectedDevices()214     public List<BluetoothDevice> getConnectedDevices() {
215         log("getConnectedDevices()");
216         final IBluetoothPbap service = mService;
217         if (service == null) {
218             Log.w(TAG, "Proxy not attached to service");
219             return new ArrayList<BluetoothDevice>();
220         }
221         try {
222             return service.getConnectedDevices();
223         } catch (RemoteException e) {
224             Log.e(TAG, e.toString());
225         }
226         return new ArrayList<BluetoothDevice>();
227     }
228 
229     /**
230      * {@inheritDoc}
231      */
232     @Override
getConnectionState(BluetoothDevice device)233     public int getConnectionState(BluetoothDevice device) {
234         log("getConnectionState: device=" + device);
235         final IBluetoothPbap service = mService;
236         if (service == null) {
237             Log.w(TAG, "Proxy not attached to service");
238             return BluetoothProfile.STATE_DISCONNECTED;
239         }
240         try {
241             return service.getConnectionState(device);
242         } catch (RemoteException e) {
243             Log.e(TAG, e.toString());
244         }
245         return BluetoothProfile.STATE_DISCONNECTED;
246     }
247 
248     /**
249      * {@inheritDoc}
250      */
251     @Override
getDevicesMatchingConnectionStates(int[] states)252     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
253         log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
254         final IBluetoothPbap service = mService;
255         if (service == null) {
256             Log.w(TAG, "Proxy not attached to service");
257             return new ArrayList<BluetoothDevice>();
258         }
259         try {
260             return service.getDevicesMatchingConnectionStates(states);
261         } catch (RemoteException e) {
262             Log.e(TAG, e.toString());
263         }
264         return new ArrayList<BluetoothDevice>();
265     }
266 
267     /**
268      * Returns true if the specified Bluetooth device is connected (does not
269      * include connecting). Returns false if not connected, or if this proxy
270      * object is not currently connected to the Pbap service.
271      */
272     // TODO: This is currently being used by SettingsLib and internal app.
isConnected(BluetoothDevice device)273     public boolean isConnected(BluetoothDevice device) {
274         return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED;
275     }
276 
277     /**
278      * Disconnects the current Pbap client (PCE). Currently this call blocks,
279      * it may soon be made asynchronous. Returns false if this proxy object is
280      * not currently connected to the Pbap service.
281      */
282     // TODO: This is currently being used by SettingsLib and will be used in the future.
283     // TODO: Must specify target device. Implement this in the service.
disconnect(BluetoothDevice device)284     public boolean disconnect(BluetoothDevice device) {
285         log("disconnect()");
286         final IBluetoothPbap service = mService;
287         if (service == null) {
288             Log.w(TAG, "Proxy not attached to service");
289             return false;
290         }
291         try {
292             service.disconnect(device);
293             return true;
294         } catch (RemoteException e) {
295             Log.e(TAG, e.toString());
296         }
297         return false;
298     }
299 
300     private final ServiceConnection mConnection = new ServiceConnection() {
301         public void onServiceConnected(ComponentName className, IBinder service) {
302             log("Proxy object connected");
303             mService = IBluetoothPbap.Stub.asInterface(service);
304             if (mServiceListener != null) {
305                 mServiceListener.onServiceConnected(BluetoothPbap.this);
306             }
307         }
308 
309         public void onServiceDisconnected(ComponentName className) {
310             log("Proxy object disconnected");
311             mService = null;
312             if (mServiceListener != null) {
313                 mServiceListener.onServiceDisconnected();
314             }
315         }
316     };
317 
log(String msg)318     private static void log(String msg) {
319         if (DBG) {
320             Log.d(TAG, msg);
321         }
322     }
323 }
324