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