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