blob: ebf6bed544754175315d0361449a68f877ffb00a [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
Mathew Inwood7acad5e2018-08-01 15:00:35 +010019import android.annotation.UnsupportedAppUsage;
Casper Bonde2a5f6082015-03-19 10:36:45 +010020import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.ServiceConnection;
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;
jovanak2a9131f2018-10-15 17:50:19 -070027import android.os.UserHandle;
Casper Bonde2a5f6082015-03-19 10:36:45 +010028import android.util.Log;
29
Jack Hea355e5e2017-08-22 16:06:54 -070030import java.util.ArrayList;
31import java.util.List;
32
Andre Eisenbachab258132015-05-04 13:28:04 -070033/**
34 * This class provides the APIs to control the Bluetooth SIM
35 * Access Profile (SAP).
36 *
37 * <p>BluetoothSap is a proxy object for controlling the Bluetooth
38 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
39 * the BluetoothSap proxy object.
40 *
41 * <p>Each method is protected with its appropriate permission.
Jack Hea355e5e2017-08-22 16:06:54 -070042 *
Andre Eisenbachab258132015-05-04 13:28:04 -070043 * @hide
44 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010045public final class BluetoothSap implements BluetoothProfile {
46
47 private static final String TAG = "BluetoothSap";
48 private static final boolean DBG = true;
49 private static final boolean VDBG = false;
50
Andre Eisenbachab258132015-05-04 13:28:04 -070051 /**
52 * Intent used to broadcast the change in connection state of the profile.
53 *
54 * <p>This intent will have 4 extras:
55 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070056 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
57 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
58 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Andre Eisenbachab258132015-05-04 13:28:04 -070059 * </ul>
60 *
61 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
62 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
63 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
64 *
65 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
66 * receive.
Jack Hea355e5e2017-08-22 16:06:54 -070067 *
Andre Eisenbachab258132015-05-04 13:28:04 -070068 * @hide
69 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010070 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070071 "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
Casper Bonde2a5f6082015-03-19 10:36:45 +010072
Jack He16eeac32017-08-17 12:11:18 -070073 private volatile IBluetoothSap mService;
Casper Bonde2a5f6082015-03-19 10:36:45 +010074 private final Context mContext;
75 private ServiceListener mServiceListener;
76 private BluetoothAdapter mAdapter;
77
Andre Eisenbachab258132015-05-04 13:28:04 -070078 /**
79 * There was an error trying to obtain the state.
Jack Hea355e5e2017-08-22 16:06:54 -070080 *
Andre Eisenbachab258132015-05-04 13:28:04 -070081 * @hide
82 */
83 public static final int STATE_ERROR = -1;
Casper Bonde2a5f6082015-03-19 10:36:45 +010084
Andre Eisenbachab258132015-05-04 13:28:04 -070085 /**
86 * Connection state change succceeded.
Jack Hea355e5e2017-08-22 16:06:54 -070087 *
Andre Eisenbachab258132015-05-04 13:28:04 -070088 * @hide
89 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010090 public static final int RESULT_SUCCESS = 1;
Andre Eisenbachab258132015-05-04 13:28:04 -070091
92 /**
93 * Connection canceled before completion.
Jack Hea355e5e2017-08-22 16:06:54 -070094 *
Andre Eisenbachab258132015-05-04 13:28:04 -070095 * @hide
96 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010097 public static final int RESULT_CANCELED = 2;
98
Jack He2992cd02017-08-22 21:21:23 -070099 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Casper Bonde2a5f6082015-03-19 10:36:45 +0100100 new IBluetoothStateChangeCallback.Stub() {
101 public void onBluetoothStateChange(boolean up) {
102 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
103 if (!up) {
Jack Hea355e5e2017-08-22 16:06:54 -0700104 if (VDBG) Log.d(TAG, "Unbinding service...");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100105 synchronized (mConnection) {
106 try {
107 mService = null;
108 mContext.unbindService(mConnection);
109 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700110 Log.e(TAG, "", re);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100111 }
112 }
113 } else {
114 synchronized (mConnection) {
115 try {
116 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700117 if (VDBG) Log.d(TAG, "Binding service...");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100118 doBind();
119 }
120 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700121 Log.e(TAG, "", re);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100122 }
123 }
124 }
125 }
Jack Hea355e5e2017-08-22 16:06:54 -0700126 };
Casper Bonde2a5f6082015-03-19 10:36:45 +0100127
128 /**
129 * Create a BluetoothSap proxy object.
130 */
131 /*package*/ BluetoothSap(Context context, ServiceListener l) {
132 if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
133 mContext = context;
134 mServiceListener = l;
135 mAdapter = BluetoothAdapter.getDefaultAdapter();
136 IBluetoothManager mgr = mAdapter.getBluetoothManager();
137 if (mgr != null) {
138 try {
139 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
140 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700141 Log.e(TAG, "", e);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100142 }
143 }
144 doBind();
145 }
146
147 boolean doBind() {
ugo_yuf308ab02016-05-30 20:56:28 +0800148 Intent intent = new Intent(IBluetoothSap.class.getName());
Casper Bonde2a5f6082015-03-19 10:36:45 +0100149 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
150 intent.setComponent(comp);
151 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
jovanak2a9131f2018-10-15 17:50:19 -0700152 UserHandle.CURRENT_OR_SELF)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100153 Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
154 return false;
155 }
156 return true;
157 }
158
159 protected void finalize() throws Throwable {
160 try {
161 close();
162 } finally {
163 super.finalize();
164 }
165 }
166
167 /**
168 * Close the connection to the backing service.
169 * Other public functions of BluetoothSap will return default error
170 * results once close() has been called. Multiple invocations of close()
171 * are ok.
Jack Hea355e5e2017-08-22 16:06:54 -0700172 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700173 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100174 */
175 public synchronized void close() {
176 IBluetoothManager mgr = mAdapter.getBluetoothManager();
177 if (mgr != null) {
178 try {
179 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
180 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700181 Log.e(TAG, "", e);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100182 }
183 }
184
185 synchronized (mConnection) {
186 if (mService != null) {
187 try {
188 mService = null;
189 mContext.unbindService(mConnection);
190 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700191 Log.e(TAG, "", re);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100192 }
193 }
194 }
195 mServiceListener = null;
196 }
197
198 /**
199 * Get the current state of the BluetoothSap service.
Jack Hea355e5e2017-08-22 16:06:54 -0700200 *
201 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
202 * connected to the Sap service.
Andre Eisenbachab258132015-05-04 13:28:04 -0700203 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100204 */
205 public int getState() {
206 if (VDBG) log("getState()");
Jack He16eeac32017-08-17 12:11:18 -0700207 final IBluetoothSap service = mService;
208 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100209 try {
Jack He16eeac32017-08-17 12:11:18 -0700210 return service.getState();
Jack Hea355e5e2017-08-22 16:06:54 -0700211 } catch (RemoteException e) {
212 Log.e(TAG, e.toString());
213 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100214 } else {
215 Log.w(TAG, "Proxy not attached to service");
216 if (DBG) log(Log.getStackTraceString(new Throwable()));
217 }
218 return BluetoothSap.STATE_ERROR;
219 }
220
221 /**
222 * Get the currently connected remote Bluetooth device (PCE).
Jack Hea355e5e2017-08-22 16:06:54 -0700223 *
224 * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
225 * this proxy object is not connected to the Sap service.
Andre Eisenbachab258132015-05-04 13:28:04 -0700226 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100227 */
228 public BluetoothDevice getClient() {
229 if (VDBG) log("getClient()");
Jack He16eeac32017-08-17 12:11:18 -0700230 final IBluetoothSap service = mService;
231 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100232 try {
Jack He16eeac32017-08-17 12:11:18 -0700233 return service.getClient();
Jack Hea355e5e2017-08-22 16:06:54 -0700234 } catch (RemoteException e) {
235 Log.e(TAG, e.toString());
236 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100237 } else {
238 Log.w(TAG, "Proxy not attached to service");
239 if (DBG) log(Log.getStackTraceString(new Throwable()));
240 }
241 return null;
242 }
243
244 /**
245 * Returns true if the specified Bluetooth device is connected.
246 * Returns false if not connected, or if this proxy object is not
247 * currently connected to the Sap service.
Jack Hea355e5e2017-08-22 16:06:54 -0700248 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700249 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100250 */
251 public boolean isConnected(BluetoothDevice device) {
252 if (VDBG) log("isConnected(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700253 final IBluetoothSap service = mService;
254 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100255 try {
Jack He16eeac32017-08-17 12:11:18 -0700256 return service.isConnected(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700257 } catch (RemoteException e) {
258 Log.e(TAG, e.toString());
259 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100260 } else {
261 Log.w(TAG, "Proxy not attached to service");
262 if (DBG) log(Log.getStackTraceString(new Throwable()));
263 }
264 return false;
265 }
266
267 /**
268 * Initiate connection. Initiation of outgoing connections is not
269 * supported for SAP server.
Jack Hea355e5e2017-08-22 16:06:54 -0700270 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700271 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100272 */
273 public boolean connect(BluetoothDevice device) {
274 if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
275 return false;
276 }
277
278 /**
279 * Initiate disconnect.
280 *
281 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700282 * @return false on error, true otherwise
Andre Eisenbachab258132015-05-04 13:28:04 -0700283 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100284 */
Mathew Inwood7acad5e2018-08-01 15:00:35 +0100285 @UnsupportedAppUsage
Casper Bonde2a5f6082015-03-19 10:36:45 +0100286 public boolean disconnect(BluetoothDevice device) {
287 if (DBG) log("disconnect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700288 final IBluetoothSap service = mService;
289 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100290 try {
Jack He16eeac32017-08-17 12:11:18 -0700291 return service.disconnect(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100292 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700293 Log.e(TAG, Log.getStackTraceString(new Throwable()));
294 return false;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100295 }
296 }
Jack He16eeac32017-08-17 12:11:18 -0700297 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100298 return false;
299 }
300
301 /**
302 * Get the list of connected devices. Currently at most one.
303 *
304 * @return list of connected devices
Andre Eisenbachab258132015-05-04 13:28:04 -0700305 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100306 */
307 public List<BluetoothDevice> getConnectedDevices() {
308 if (DBG) log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700309 final IBluetoothSap service = mService;
310 if (service != null && isEnabled()) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100311 try {
Jack He16eeac32017-08-17 12:11:18 -0700312 return service.getConnectedDevices();
Casper Bonde2a5f6082015-03-19 10:36:45 +0100313 } catch (RemoteException e) {
314 Log.e(TAG, Log.getStackTraceString(new Throwable()));
315 return new ArrayList<BluetoothDevice>();
316 }
317 }
Jack He16eeac32017-08-17 12:11:18 -0700318 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100319 return new ArrayList<BluetoothDevice>();
320 }
321
322 /**
323 * Get the list of devices matching specified states. Currently at most one.
324 *
325 * @return list of matching devices
Andre Eisenbachab258132015-05-04 13:28:04 -0700326 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100327 */
328 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
329 if (DBG) log("getDevicesMatchingStates()");
Jack He16eeac32017-08-17 12:11:18 -0700330 final IBluetoothSap service = mService;
331 if (service != null && isEnabled()) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100332 try {
Jack He16eeac32017-08-17 12:11:18 -0700333 return service.getDevicesMatchingConnectionStates(states);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100334 } catch (RemoteException e) {
335 Log.e(TAG, Log.getStackTraceString(new Throwable()));
336 return new ArrayList<BluetoothDevice>();
337 }
338 }
Jack He16eeac32017-08-17 12:11:18 -0700339 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100340 return new ArrayList<BluetoothDevice>();
341 }
342
343 /**
344 * Get connection state of device
345 *
346 * @return device connection state
Andre Eisenbachab258132015-05-04 13:28:04 -0700347 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100348 */
349 public int getConnectionState(BluetoothDevice device) {
350 if (DBG) log("getConnectionState(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700351 final IBluetoothSap service = mService;
352 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100353 try {
Jack He16eeac32017-08-17 12:11:18 -0700354 return service.getConnectionState(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100355 } catch (RemoteException e) {
356 Log.e(TAG, Log.getStackTraceString(new Throwable()));
357 return BluetoothProfile.STATE_DISCONNECTED;
358 }
359 }
Jack He16eeac32017-08-17 12:11:18 -0700360 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100361 return BluetoothProfile.STATE_DISCONNECTED;
362 }
363
364 /**
365 * Set priority of the profile
366 *
367 * <p> The device should already be paired.
Casper Bonde2a5f6082015-03-19 10:36:45 +0100368 *
369 * @param device Paired bluetooth device
370 * @param priority
371 * @return true if priority is set, false on error
Andre Eisenbachab258132015-05-04 13:28:04 -0700372 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100373 */
374 public boolean setPriority(BluetoothDevice device, int priority) {
375 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Jack He16eeac32017-08-17 12:11:18 -0700376 final IBluetoothSap service = mService;
377 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700378 if (priority != BluetoothProfile.PRIORITY_OFF
379 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700380 return false;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100381 }
382 try {
Jack He16eeac32017-08-17 12:11:18 -0700383 return service.setPriority(device, priority);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100384 } catch (RemoteException e) {
385 Log.e(TAG, Log.getStackTraceString(new Throwable()));
386 return false;
387 }
388 }
Jack He16eeac32017-08-17 12:11:18 -0700389 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100390 return false;
391 }
392
393 /**
394 * Get the priority of the profile.
395 *
Casper Bonde2a5f6082015-03-19 10:36:45 +0100396 * @param device Bluetooth device
397 * @return priority of the device
Andre Eisenbachab258132015-05-04 13:28:04 -0700398 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100399 */
400 public int getPriority(BluetoothDevice device) {
401 if (VDBG) log("getPriority(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700402 final IBluetoothSap service = mService;
403 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100404 try {
Jack He16eeac32017-08-17 12:11:18 -0700405 return service.getPriority(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100406 } catch (RemoteException e) {
407 Log.e(TAG, Log.getStackTraceString(new Throwable()));
408 return PRIORITY_OFF;
409 }
410 }
Jack He16eeac32017-08-17 12:11:18 -0700411 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100412 return PRIORITY_OFF;
413 }
414
Jack He16eeac32017-08-17 12:11:18 -0700415 private final ServiceConnection mConnection = new ServiceConnection() {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100416 public void onServiceConnected(ComponentName className, IBinder service) {
417 if (DBG) log("Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600418 mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
Casper Bonde2a5f6082015-03-19 10:36:45 +0100419 if (mServiceListener != null) {
420 mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
421 }
422 }
Jack Hea355e5e2017-08-22 16:06:54 -0700423
Casper Bonde2a5f6082015-03-19 10:36:45 +0100424 public void onServiceDisconnected(ComponentName className) {
425 if (DBG) log("Proxy object disconnected");
426 mService = null;
427 if (mServiceListener != null) {
428 mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
429 }
430 }
431 };
432
433 private static void log(String msg) {
434 Log.d(TAG, msg);
435 }
436
437 private boolean isEnabled() {
438 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
439
Jack Hea355e5e2017-08-22 16:06:54 -0700440 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100441 return true;
Jack Hea355e5e2017-08-22 16:06:54 -0700442 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100443 log("Bluetooth is Not enabled");
444 return false;
445 }
446
Jack He16eeac32017-08-17 12:11:18 -0700447 private static boolean isValidDevice(BluetoothDevice device) {
448 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Casper Bonde2a5f6082015-03-19 10:36:45 +0100449 }
450
451}