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