• 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.Binder;
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.List;
30 
31 /**
32  * This class provides the APIs to control the Bluetooth SIM
33  * Access Profile (SAP).
34  *
35  * <p>BluetoothSap is a proxy object for controlling the Bluetooth
36  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
37  * the BluetoothSap proxy object.
38  *
39  * <p>Each method is protected with its appropriate permission.
40  *
41  * @hide
42  */
43 public final class BluetoothSap implements BluetoothProfile {
44 
45     private static final String TAG = "BluetoothSap";
46     private static final boolean DBG = true;
47     private static final boolean VDBG = false;
48 
49     /**
50      * Intent used to broadcast the change in connection state of the profile.
51      *
52      * <p>This intent will have 4 extras:
53      * <ul>
54      * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
55      * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
56      * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
57      * </ul>
58      *
59      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
60      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
61      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
62      *
63      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
64      * receive.
65      *
66      * @hide
67      */
68     public static final String ACTION_CONNECTION_STATE_CHANGED =
69             "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
70 
71     private volatile IBluetoothSap mService;
72     private final Context mContext;
73     private ServiceListener mServiceListener;
74     private BluetoothAdapter mAdapter;
75 
76     /**
77      * There was an error trying to obtain the state.
78      *
79      * @hide
80      */
81     public static final int STATE_ERROR = -1;
82 
83     /**
84      * Connection state change succceeded.
85      *
86      * @hide
87      */
88     public static final int RESULT_SUCCESS = 1;
89 
90     /**
91      * Connection canceled before completion.
92      *
93      * @hide
94      */
95     public static final int RESULT_CANCELED = 2;
96 
97     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
98             new IBluetoothStateChangeCallback.Stub() {
99                 public void onBluetoothStateChange(boolean up) {
100                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
101                     if (!up) {
102                         if (VDBG) Log.d(TAG, "Unbinding service...");
103                         synchronized (mConnection) {
104                             try {
105                                 mService = null;
106                                 mContext.unbindService(mConnection);
107                             } catch (Exception re) {
108                                 Log.e(TAG, "", re);
109                             }
110                         }
111                     } else {
112                         synchronized (mConnection) {
113                             try {
114                                 if (mService == null) {
115                                     if (VDBG) Log.d(TAG, "Binding service...");
116                                     doBind();
117                                 }
118                             } catch (Exception re) {
119                                 Log.e(TAG, "", re);
120                             }
121                         }
122                     }
123                 }
124             };
125 
126     /**
127      * Create a BluetoothSap proxy object.
128      */
BluetoothSap(Context context, ServiceListener l)129     /*package*/ BluetoothSap(Context context, ServiceListener l) {
130         if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
131         mContext = context;
132         mServiceListener = l;
133         mAdapter = BluetoothAdapter.getDefaultAdapter();
134         IBluetoothManager mgr = mAdapter.getBluetoothManager();
135         if (mgr != null) {
136             try {
137                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
138             } catch (RemoteException e) {
139                 Log.e(TAG, "", e);
140             }
141         }
142         doBind();
143     }
144 
doBind()145     boolean doBind() {
146         Intent intent = new Intent(IBluetoothSap.class.getName());
147         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
148         intent.setComponent(comp);
149         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
150                 mContext.getUser())) {
151             Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
152             return false;
153         }
154         return true;
155     }
156 
finalize()157     protected void finalize() throws Throwable {
158         try {
159             close();
160         } finally {
161             super.finalize();
162         }
163     }
164 
165     /**
166      * Close the connection to the backing service.
167      * Other public functions of BluetoothSap will return default error
168      * results once close() has been called. Multiple invocations of close()
169      * are ok.
170      *
171      * @hide
172      */
close()173     public synchronized void close() {
174         IBluetoothManager mgr = mAdapter.getBluetoothManager();
175         if (mgr != null) {
176             try {
177                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
178             } catch (Exception e) {
179                 Log.e(TAG, "", e);
180             }
181         }
182 
183         synchronized (mConnection) {
184             if (mService != null) {
185                 try {
186                     mService = null;
187                     mContext.unbindService(mConnection);
188                 } catch (Exception re) {
189                     Log.e(TAG, "", re);
190                 }
191             }
192         }
193         mServiceListener = null;
194     }
195 
196     /**
197      * Get the current state of the BluetoothSap service.
198      *
199      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
200      * connected to the Sap service.
201      * @hide
202      */
getState()203     public int getState() {
204         if (VDBG) log("getState()");
205         final IBluetoothSap service = mService;
206         if (service != null) {
207             try {
208                 return service.getState();
209             } catch (RemoteException e) {
210                 Log.e(TAG, e.toString());
211             }
212         } else {
213             Log.w(TAG, "Proxy not attached to service");
214             if (DBG) log(Log.getStackTraceString(new Throwable()));
215         }
216         return BluetoothSap.STATE_ERROR;
217     }
218 
219     /**
220      * Get the currently connected remote Bluetooth device (PCE).
221      *
222      * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
223      * this proxy object is not connected to the Sap service.
224      * @hide
225      */
getClient()226     public BluetoothDevice getClient() {
227         if (VDBG) log("getClient()");
228         final IBluetoothSap service = mService;
229         if (service != null) {
230             try {
231                 return service.getClient();
232             } catch (RemoteException e) {
233                 Log.e(TAG, e.toString());
234             }
235         } else {
236             Log.w(TAG, "Proxy not attached to service");
237             if (DBG) log(Log.getStackTraceString(new Throwable()));
238         }
239         return null;
240     }
241 
242     /**
243      * Returns true if the specified Bluetooth device is connected.
244      * Returns false if not connected, or if this proxy object is not
245      * currently connected to the Sap service.
246      *
247      * @hide
248      */
isConnected(BluetoothDevice device)249     public boolean isConnected(BluetoothDevice device) {
250         if (VDBG) log("isConnected(" + device + ")");
251         final IBluetoothSap service = mService;
252         if (service != null) {
253             try {
254                 return service.isConnected(device);
255             } catch (RemoteException e) {
256                 Log.e(TAG, e.toString());
257             }
258         } else {
259             Log.w(TAG, "Proxy not attached to service");
260             if (DBG) log(Log.getStackTraceString(new Throwable()));
261         }
262         return false;
263     }
264 
265     /**
266      * Initiate connection. Initiation of outgoing connections is not
267      * supported for SAP server.
268      *
269      * @hide
270      */
connect(BluetoothDevice device)271     public boolean connect(BluetoothDevice device) {
272         if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
273         return false;
274     }
275 
276     /**
277      * Initiate disconnect.
278      *
279      * @param device Remote Bluetooth Device
280      * @return false on error, true otherwise
281      * @hide
282      */
disconnect(BluetoothDevice device)283     public boolean disconnect(BluetoothDevice device) {
284         if (DBG) log("disconnect(" + device + ")");
285         final IBluetoothSap service = mService;
286         if (service != null && isEnabled() && isValidDevice(device)) {
287             try {
288                 return service.disconnect(device);
289             } catch (RemoteException e) {
290                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
291                 return false;
292             }
293         }
294         if (service == null) Log.w(TAG, "Proxy not attached to service");
295         return false;
296     }
297 
298     /**
299      * Get the list of connected devices. Currently at most one.
300      *
301      * @return list of connected devices
302      * @hide
303      */
getConnectedDevices()304     public List<BluetoothDevice> getConnectedDevices() {
305         if (DBG) log("getConnectedDevices()");
306         final IBluetoothSap service = mService;
307         if (service != null && isEnabled()) {
308             try {
309                 return service.getConnectedDevices();
310             } catch (RemoteException e) {
311                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
312                 return new ArrayList<BluetoothDevice>();
313             }
314         }
315         if (service == null) Log.w(TAG, "Proxy not attached to service");
316         return new ArrayList<BluetoothDevice>();
317     }
318 
319     /**
320      * Get the list of devices matching specified states. Currently at most one.
321      *
322      * @return list of matching devices
323      * @hide
324      */
getDevicesMatchingConnectionStates(int[] states)325     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
326         if (DBG) log("getDevicesMatchingStates()");
327         final IBluetoothSap service = mService;
328         if (service != null && isEnabled()) {
329             try {
330                 return service.getDevicesMatchingConnectionStates(states);
331             } catch (RemoteException e) {
332                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
333                 return new ArrayList<BluetoothDevice>();
334             }
335         }
336         if (service == null) Log.w(TAG, "Proxy not attached to service");
337         return new ArrayList<BluetoothDevice>();
338     }
339 
340     /**
341      * Get connection state of device
342      *
343      * @return device connection state
344      * @hide
345      */
getConnectionState(BluetoothDevice device)346     public int getConnectionState(BluetoothDevice device) {
347         if (DBG) log("getConnectionState(" + device + ")");
348         final IBluetoothSap service = mService;
349         if (service != null && isEnabled() && isValidDevice(device)) {
350             try {
351                 return service.getConnectionState(device);
352             } catch (RemoteException e) {
353                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
354                 return BluetoothProfile.STATE_DISCONNECTED;
355             }
356         }
357         if (service == null) Log.w(TAG, "Proxy not attached to service");
358         return BluetoothProfile.STATE_DISCONNECTED;
359     }
360 
361     /**
362      * Set priority of the profile
363      *
364      * <p> The device should already be paired.
365      *
366      * @param device Paired bluetooth device
367      * @param priority
368      * @return true if priority is set, false on error
369      * @hide
370      */
setPriority(BluetoothDevice device, int priority)371     public boolean setPriority(BluetoothDevice device, int priority) {
372         if (DBG) log("setPriority(" + device + ", " + priority + ")");
373         final IBluetoothSap service = mService;
374         if (service != null && isEnabled() && isValidDevice(device)) {
375             if (priority != BluetoothProfile.PRIORITY_OFF
376                     && priority != BluetoothProfile.PRIORITY_ON) {
377                 return false;
378             }
379             try {
380                 return service.setPriority(device, priority);
381             } catch (RemoteException e) {
382                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
383                 return false;
384             }
385         }
386         if (service == null) Log.w(TAG, "Proxy not attached to service");
387         return false;
388     }
389 
390     /**
391      * Get the priority of the profile.
392      *
393      * @param device Bluetooth device
394      * @return priority of the device
395      * @hide
396      */
getPriority(BluetoothDevice device)397     public int getPriority(BluetoothDevice device) {
398         if (VDBG) log("getPriority(" + device + ")");
399         final IBluetoothSap service = mService;
400         if (service != null && isEnabled() && isValidDevice(device)) {
401             try {
402                 return service.getPriority(device);
403             } catch (RemoteException e) {
404                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
405                 return PRIORITY_OFF;
406             }
407         }
408         if (service == null) Log.w(TAG, "Proxy not attached to service");
409         return PRIORITY_OFF;
410     }
411 
412     private final ServiceConnection mConnection = new ServiceConnection() {
413         public void onServiceConnected(ComponentName className, IBinder service) {
414             if (DBG) log("Proxy object connected");
415             mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
416             if (mServiceListener != null) {
417                 mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
418             }
419         }
420 
421         public void onServiceDisconnected(ComponentName className) {
422             if (DBG) log("Proxy object disconnected");
423             mService = null;
424             if (mServiceListener != null) {
425                 mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
426             }
427         }
428     };
429 
log(String msg)430     private static void log(String msg) {
431         Log.d(TAG, msg);
432     }
433 
isEnabled()434     private boolean isEnabled() {
435         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
436 
437         if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
438             return true;
439         }
440         log("Bluetooth is Not enabled");
441         return false;
442     }
443 
isValidDevice(BluetoothDevice device)444     private static boolean isValidDevice(BluetoothDevice device) {
445         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
446     }
447 
448 }
449