blob: 9b4dabcdcfdc94d817d55389343d4dc49736d424 [file] [log] [blame]
Casper Bonde2a5f6082015-03-19 10:36:45 +01001/*
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
17package android.bluetooth;
18
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -080019import android.Manifest;
20import android.annotation.RequiresPermission;
21import android.annotation.SystemApi;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010022import android.annotation.UnsupportedAppUsage;
Casper Bonde2a5f6082015-03-19 10:36:45 +010023import android.content.Context;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060024import android.os.Binder;
Casper Bonde2a5f6082015-03-19 10:36:45 +010025import android.os.IBinder;
Jack Hea355e5e2017-08-22 16:06:54 -070026import android.os.RemoteException;
Casper Bonde2a5f6082015-03-19 10:36:45 +010027import android.util.Log;
28
Jack Hea355e5e2017-08-22 16:06:54 -070029import java.util.ArrayList;
30import java.util.List;
31
Andre Eisenbachab258132015-05-04 13:28:04 -070032/**
33 * This class provides the APIs to control the Bluetooth SIM
34 * Access Profile (SAP).
35 *
36 * <p>BluetoothSap is a proxy object for controlling the Bluetooth
37 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
38 * the BluetoothSap proxy object.
39 *
40 * <p>Each method is protected with its appropriate permission.
Jack Hea355e5e2017-08-22 16:06:54 -070041 *
Andre Eisenbachab258132015-05-04 13:28:04 -070042 * @hide
43 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010044public final class BluetoothSap implements BluetoothProfile {
45
46 private static final String TAG = "BluetoothSap";
47 private static final boolean DBG = true;
48 private static final boolean VDBG = false;
49
Andre Eisenbachab258132015-05-04 13:28:04 -070050 /**
51 * Intent used to broadcast the change in connection state of the profile.
52 *
53 * <p>This intent will have 4 extras:
54 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070055 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
56 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
57 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Andre Eisenbachab258132015-05-04 13:28:04 -070058 * </ul>
59 *
60 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
61 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
62 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
63 *
64 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
65 * receive.
Jack Hea355e5e2017-08-22 16:06:54 -070066 *
Andre Eisenbachab258132015-05-04 13:28:04 -070067 * @hide
68 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010069 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070070 "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
Casper Bonde2a5f6082015-03-19 10:36:45 +010071
Andre Eisenbachab258132015-05-04 13:28:04 -070072 /**
73 * There was an error trying to obtain the state.
Jack Hea355e5e2017-08-22 16:06:54 -070074 *
Andre Eisenbachab258132015-05-04 13:28:04 -070075 * @hide
76 */
77 public static final int STATE_ERROR = -1;
Casper Bonde2a5f6082015-03-19 10:36:45 +010078
Andre Eisenbachab258132015-05-04 13:28:04 -070079 /**
80 * Connection state change succceeded.
Jack Hea355e5e2017-08-22 16:06:54 -070081 *
Andre Eisenbachab258132015-05-04 13:28:04 -070082 * @hide
83 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010084 public static final int RESULT_SUCCESS = 1;
Andre Eisenbachab258132015-05-04 13:28:04 -070085
86 /**
87 * Connection canceled before completion.
Jack Hea355e5e2017-08-22 16:06:54 -070088 *
Andre Eisenbachab258132015-05-04 13:28:04 -070089 * @hide
90 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010091 public static final int RESULT_CANCELED = 2;
92
Ugo Yud0115462019-03-26 21:38:08 +080093 private BluetoothAdapter mAdapter;
94 private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector =
95 new BluetoothProfileConnector(this, BluetoothProfile.SAP,
96 "BluetoothSap", IBluetoothSap.class.getName()) {
97 @Override
98 public IBluetoothSap getServiceInterface(IBinder service) {
99 return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
Casper Bonde2a5f6082015-03-19 10:36:45 +0100100 }
Ugo Yud0115462019-03-26 21:38:08 +0800101 };
Casper Bonde2a5f6082015-03-19 10:36:45 +0100102
103 /**
104 * Create a BluetoothSap proxy object.
105 */
Ugo Yud0115462019-03-26 21:38:08 +0800106 /*package*/ BluetoothSap(Context context, ServiceListener listener) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100107 if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100108 mAdapter = BluetoothAdapter.getDefaultAdapter();
Ugo Yud0115462019-03-26 21:38:08 +0800109 mProfileConnector.connect(context, listener);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100110 }
111
112 protected void finalize() throws Throwable {
113 try {
114 close();
115 } finally {
116 super.finalize();
117 }
118 }
119
120 /**
121 * Close the connection to the backing service.
122 * Other public functions of BluetoothSap will return default error
123 * results once close() has been called. Multiple invocations of close()
124 * are ok.
Jack Hea355e5e2017-08-22 16:06:54 -0700125 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700126 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100127 */
128 public synchronized void close() {
Ugo Yud0115462019-03-26 21:38:08 +0800129 mProfileConnector.disconnect();
130 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100131
Ugo Yud0115462019-03-26 21:38:08 +0800132 private IBluetoothSap getService() {
133 return mProfileConnector.getService();
Casper Bonde2a5f6082015-03-19 10:36:45 +0100134 }
135
136 /**
137 * Get the current state of the BluetoothSap service.
Jack Hea355e5e2017-08-22 16:06:54 -0700138 *
139 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
140 * connected to the Sap service.
Andre Eisenbachab258132015-05-04 13:28:04 -0700141 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100142 */
143 public int getState() {
144 if (VDBG) log("getState()");
Ugo Yud0115462019-03-26 21:38:08 +0800145 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700146 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100147 try {
Jack He16eeac32017-08-17 12:11:18 -0700148 return service.getState();
Jack Hea355e5e2017-08-22 16:06:54 -0700149 } catch (RemoteException e) {
150 Log.e(TAG, e.toString());
151 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100152 } else {
153 Log.w(TAG, "Proxy not attached to service");
154 if (DBG) log(Log.getStackTraceString(new Throwable()));
155 }
156 return BluetoothSap.STATE_ERROR;
157 }
158
159 /**
160 * Get the currently connected remote Bluetooth device (PCE).
Jack Hea355e5e2017-08-22 16:06:54 -0700161 *
162 * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
163 * this proxy object is not connected to the Sap service.
Andre Eisenbachab258132015-05-04 13:28:04 -0700164 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100165 */
166 public BluetoothDevice getClient() {
167 if (VDBG) log("getClient()");
Ugo Yud0115462019-03-26 21:38:08 +0800168 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700169 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100170 try {
Jack He16eeac32017-08-17 12:11:18 -0700171 return service.getClient();
Jack Hea355e5e2017-08-22 16:06:54 -0700172 } catch (RemoteException e) {
173 Log.e(TAG, e.toString());
174 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100175 } else {
176 Log.w(TAG, "Proxy not attached to service");
177 if (DBG) log(Log.getStackTraceString(new Throwable()));
178 }
179 return null;
180 }
181
182 /**
183 * Returns true if the specified Bluetooth device is connected.
184 * Returns false if not connected, or if this proxy object is not
185 * currently connected to the Sap service.
Jack Hea355e5e2017-08-22 16:06:54 -0700186 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700187 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100188 */
189 public boolean isConnected(BluetoothDevice device) {
190 if (VDBG) log("isConnected(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800191 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700192 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100193 try {
Jack He16eeac32017-08-17 12:11:18 -0700194 return service.isConnected(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700195 } catch (RemoteException e) {
196 Log.e(TAG, e.toString());
197 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100198 } else {
199 Log.w(TAG, "Proxy not attached to service");
200 if (DBG) log(Log.getStackTraceString(new Throwable()));
201 }
202 return false;
203 }
204
205 /**
206 * Initiate connection. Initiation of outgoing connections is not
207 * supported for SAP server.
Jack Hea355e5e2017-08-22 16:06:54 -0700208 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700209 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100210 */
211 public boolean connect(BluetoothDevice device) {
212 if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
213 return false;
214 }
215
216 /**
217 * Initiate disconnect.
218 *
219 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700220 * @return false on error, true otherwise
Andre Eisenbachab258132015-05-04 13:28:04 -0700221 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100222 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100223 @UnsupportedAppUsage
Casper Bonde2a5f6082015-03-19 10:36:45 +0100224 public boolean disconnect(BluetoothDevice device) {
225 if (DBG) log("disconnect(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800226 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700227 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100228 try {
Jack He16eeac32017-08-17 12:11:18 -0700229 return service.disconnect(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100230 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700231 Log.e(TAG, Log.getStackTraceString(new Throwable()));
232 return false;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100233 }
234 }
Jack He16eeac32017-08-17 12:11:18 -0700235 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100236 return false;
237 }
238
239 /**
240 * Get the list of connected devices. Currently at most one.
241 *
242 * @return list of connected devices
Andre Eisenbachab258132015-05-04 13:28:04 -0700243 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100244 */
245 public List<BluetoothDevice> getConnectedDevices() {
246 if (DBG) log("getConnectedDevices()");
Ugo Yud0115462019-03-26 21:38:08 +0800247 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700248 if (service != null && isEnabled()) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100249 try {
Jack He16eeac32017-08-17 12:11:18 -0700250 return service.getConnectedDevices();
Casper Bonde2a5f6082015-03-19 10:36:45 +0100251 } catch (RemoteException e) {
252 Log.e(TAG, Log.getStackTraceString(new Throwable()));
253 return new ArrayList<BluetoothDevice>();
254 }
255 }
Jack He16eeac32017-08-17 12:11:18 -0700256 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100257 return new ArrayList<BluetoothDevice>();
258 }
259
260 /**
261 * Get the list of devices matching specified states. Currently at most one.
262 *
263 * @return list of matching devices
Andre Eisenbachab258132015-05-04 13:28:04 -0700264 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100265 */
266 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
267 if (DBG) log("getDevicesMatchingStates()");
Ugo Yud0115462019-03-26 21:38:08 +0800268 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700269 if (service != null && isEnabled()) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100270 try {
Jack He16eeac32017-08-17 12:11:18 -0700271 return service.getDevicesMatchingConnectionStates(states);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100272 } catch (RemoteException e) {
273 Log.e(TAG, Log.getStackTraceString(new Throwable()));
274 return new ArrayList<BluetoothDevice>();
275 }
276 }
Jack He16eeac32017-08-17 12:11:18 -0700277 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100278 return new ArrayList<BluetoothDevice>();
279 }
280
281 /**
282 * Get connection state of device
283 *
284 * @return device connection state
Andre Eisenbachab258132015-05-04 13:28:04 -0700285 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100286 */
287 public int getConnectionState(BluetoothDevice device) {
288 if (DBG) log("getConnectionState(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800289 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700290 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100291 try {
Jack He16eeac32017-08-17 12:11:18 -0700292 return service.getConnectionState(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100293 } catch (RemoteException e) {
294 Log.e(TAG, Log.getStackTraceString(new Throwable()));
295 return BluetoothProfile.STATE_DISCONNECTED;
296 }
297 }
Jack He16eeac32017-08-17 12:11:18 -0700298 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100299 return BluetoothProfile.STATE_DISCONNECTED;
300 }
301
302 /**
303 * Set priority of the profile
304 *
305 * <p> The device should already be paired.
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800306 * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
Casper Bonde2a5f6082015-03-19 10:36:45 +0100307 *
308 * @param device Paired bluetooth device
309 * @param priority
310 * @return true if priority is set, false on error
Andre Eisenbachab258132015-05-04 13:28:04 -0700311 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100312 */
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800313 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Casper Bonde2a5f6082015-03-19 10:36:45 +0100314 public boolean setPriority(BluetoothDevice device, int priority) {
315 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800316 return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
317 }
318
319 /**
320 * Set connection policy of the profile
321 *
322 * <p> The device should already be paired.
323 * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
324 * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
325 *
326 * @param device Paired bluetooth device
327 * @param connectionPolicy is the connection policy to set to for this profile
328 * @return true if connectionPolicy is set, false on error
329 * @hide
330 */
331 @SystemApi
332 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
333 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
334 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800335 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700336 if (service != null && isEnabled() && isValidDevice(device)) {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800337 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
338 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
Jack Hea355e5e2017-08-22 16:06:54 -0700339 return false;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100340 }
341 try {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800342 return service.setConnectionPolicy(device, connectionPolicy);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100343 } catch (RemoteException e) {
344 Log.e(TAG, Log.getStackTraceString(new Throwable()));
345 return false;
346 }
347 }
Jack He16eeac32017-08-17 12:11:18 -0700348 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100349 return false;
350 }
351
352 /**
353 * Get the priority of the profile.
354 *
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800355 * <p> The priority can be any of:
356 * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
357 *
Casper Bonde2a5f6082015-03-19 10:36:45 +0100358 * @param device Bluetooth device
359 * @return priority of the device
Andre Eisenbachab258132015-05-04 13:28:04 -0700360 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100361 */
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800362 @RequiresPermission(Manifest.permission.BLUETOOTH)
Casper Bonde2a5f6082015-03-19 10:36:45 +0100363 public int getPriority(BluetoothDevice device) {
364 if (VDBG) log("getPriority(" + device + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800365 return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
366 }
367
368 /**
369 * Get the connection policy of the profile.
370 *
371 * <p> The connection policy can be any of:
372 * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
373 * {@link #CONNECTION_POLICY_UNKNOWN}
374 *
375 * @param device Bluetooth device
376 * @return connection policy of the device
377 * @hide
378 */
379 @SystemApi
380 @RequiresPermission(Manifest.permission.BLUETOOTH)
381 public int getConnectionPolicy(BluetoothDevice device) {
382 if (VDBG) log("getConnectionPolicy(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800383 final IBluetoothSap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700384 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100385 try {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800386 return service.getConnectionPolicy(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100387 } catch (RemoteException e) {
388 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800389 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100390 }
391 }
Jack He16eeac32017-08-17 12:11:18 -0700392 if (service == null) Log.w(TAG, "Proxy not attached to service");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800393 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100394 }
395
Casper Bonde2a5f6082015-03-19 10:36:45 +0100396 private static void log(String msg) {
397 Log.d(TAG, msg);
398 }
399
400 private boolean isEnabled() {
401 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
402
Jack Hea355e5e2017-08-22 16:06:54 -0700403 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100404 return true;
Jack Hea355e5e2017-08-22 16:06:54 -0700405 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100406 log("Bluetooth is Not enabled");
407 return false;
408 }
409
Jack He16eeac32017-08-17 12:11:18 -0700410 private static boolean isValidDevice(BluetoothDevice device) {
411 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Casper Bonde2a5f6082015-03-19 10:36:45 +0100412 }
413
414}