blob: 1b732062f6144a6d881375b4ed8cdcb6903cfca4 [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 Inwood4dc66d32018-08-01 15:07:20 +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;
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
Jack He16eeac32017-08-17 12:11:18 -070072 private volatile IBluetoothSap mService;
Casper Bonde2a5f6082015-03-19 10:36:45 +010073 private final Context mContext;
74 private ServiceListener mServiceListener;
75 private BluetoothAdapter mAdapter;
76
Andre Eisenbachab258132015-05-04 13:28:04 -070077 /**
78 * There was an error trying to obtain the state.
Jack Hea355e5e2017-08-22 16:06:54 -070079 *
Andre Eisenbachab258132015-05-04 13:28:04 -070080 * @hide
81 */
82 public static final int STATE_ERROR = -1;
Casper Bonde2a5f6082015-03-19 10:36:45 +010083
Andre Eisenbachab258132015-05-04 13:28:04 -070084 /**
85 * Connection state change succceeded.
Jack Hea355e5e2017-08-22 16:06:54 -070086 *
Andre Eisenbachab258132015-05-04 13:28:04 -070087 * @hide
88 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010089 public static final int RESULT_SUCCESS = 1;
Andre Eisenbachab258132015-05-04 13:28:04 -070090
91 /**
92 * Connection canceled before completion.
Jack Hea355e5e2017-08-22 16:06:54 -070093 *
Andre Eisenbachab258132015-05-04 13:28:04 -070094 * @hide
95 */
Casper Bonde2a5f6082015-03-19 10:36:45 +010096 public static final int RESULT_CANCELED = 2;
97
Jack He2992cd02017-08-22 21:21:23 -070098 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Casper Bonde2a5f6082015-03-19 10:36:45 +010099 new IBluetoothStateChangeCallback.Stub() {
100 public void onBluetoothStateChange(boolean up) {
101 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
102 if (!up) {
Jack Hea355e5e2017-08-22 16:06:54 -0700103 if (VDBG) Log.d(TAG, "Unbinding service...");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100104 synchronized (mConnection) {
105 try {
106 mService = null;
107 mContext.unbindService(mConnection);
108 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700109 Log.e(TAG, "", re);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100110 }
111 }
112 } else {
113 synchronized (mConnection) {
114 try {
115 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700116 if (VDBG) Log.d(TAG, "Binding service...");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100117 doBind();
118 }
119 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700120 Log.e(TAG, "", re);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100121 }
122 }
123 }
124 }
Jack Hea355e5e2017-08-22 16:06:54 -0700125 };
Casper Bonde2a5f6082015-03-19 10:36:45 +0100126
127 /**
128 * Create a BluetoothSap proxy object.
129 */
130 /*package*/ BluetoothSap(Context context, ServiceListener l) {
131 if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
132 mContext = context;
133 mServiceListener = l;
134 mAdapter = BluetoothAdapter.getDefaultAdapter();
135 IBluetoothManager mgr = mAdapter.getBluetoothManager();
136 if (mgr != null) {
137 try {
138 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
139 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700140 Log.e(TAG, "", e);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100141 }
142 }
143 doBind();
144 }
145
146 boolean doBind() {
ugo_yuf308ab02016-05-30 20:56:28 +0800147 Intent intent = new Intent(IBluetoothSap.class.getName());
Casper Bonde2a5f6082015-03-19 10:36:45 +0100148 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
149 intent.setComponent(comp);
150 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700151 mContext.getUser())) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100152 Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
153 return false;
154 }
155 return true;
156 }
157
158 protected void finalize() throws Throwable {
159 try {
160 close();
161 } finally {
162 super.finalize();
163 }
164 }
165
166 /**
167 * Close the connection to the backing service.
168 * Other public functions of BluetoothSap will return default error
169 * results once close() has been called. Multiple invocations of close()
170 * are ok.
Jack Hea355e5e2017-08-22 16:06:54 -0700171 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700172 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100173 */
174 public synchronized void close() {
175 IBluetoothManager mgr = mAdapter.getBluetoothManager();
176 if (mgr != null) {
177 try {
178 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
179 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700180 Log.e(TAG, "", e);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100181 }
182 }
183
184 synchronized (mConnection) {
185 if (mService != null) {
186 try {
187 mService = null;
188 mContext.unbindService(mConnection);
189 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700190 Log.e(TAG, "", re);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100191 }
192 }
193 }
194 mServiceListener = null;
195 }
196
197 /**
198 * Get the current state of the BluetoothSap service.
Jack Hea355e5e2017-08-22 16:06:54 -0700199 *
200 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
201 * connected to the Sap service.
Andre Eisenbachab258132015-05-04 13:28:04 -0700202 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100203 */
204 public int getState() {
205 if (VDBG) log("getState()");
Jack He16eeac32017-08-17 12:11:18 -0700206 final IBluetoothSap service = mService;
207 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100208 try {
Jack He16eeac32017-08-17 12:11:18 -0700209 return service.getState();
Jack Hea355e5e2017-08-22 16:06:54 -0700210 } catch (RemoteException e) {
211 Log.e(TAG, e.toString());
212 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100213 } else {
214 Log.w(TAG, "Proxy not attached to service");
215 if (DBG) log(Log.getStackTraceString(new Throwable()));
216 }
217 return BluetoothSap.STATE_ERROR;
218 }
219
220 /**
221 * Get the currently connected remote Bluetooth device (PCE).
Jack Hea355e5e2017-08-22 16:06:54 -0700222 *
223 * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
224 * this proxy object is not connected to the Sap service.
Andre Eisenbachab258132015-05-04 13:28:04 -0700225 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100226 */
227 public BluetoothDevice getClient() {
228 if (VDBG) log("getClient()");
Jack He16eeac32017-08-17 12:11:18 -0700229 final IBluetoothSap service = mService;
230 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100231 try {
Jack He16eeac32017-08-17 12:11:18 -0700232 return service.getClient();
Jack Hea355e5e2017-08-22 16:06:54 -0700233 } catch (RemoteException e) {
234 Log.e(TAG, e.toString());
235 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100236 } else {
237 Log.w(TAG, "Proxy not attached to service");
238 if (DBG) log(Log.getStackTraceString(new Throwable()));
239 }
240 return null;
241 }
242
243 /**
244 * Returns true if the specified Bluetooth device is connected.
245 * Returns false if not connected, or if this proxy object is not
246 * currently connected to the Sap service.
Jack Hea355e5e2017-08-22 16:06:54 -0700247 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700248 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100249 */
250 public boolean isConnected(BluetoothDevice device) {
251 if (VDBG) log("isConnected(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700252 final IBluetoothSap service = mService;
253 if (service != null) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100254 try {
Jack He16eeac32017-08-17 12:11:18 -0700255 return service.isConnected(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700256 } catch (RemoteException e) {
257 Log.e(TAG, e.toString());
258 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100259 } else {
260 Log.w(TAG, "Proxy not attached to service");
261 if (DBG) log(Log.getStackTraceString(new Throwable()));
262 }
263 return false;
264 }
265
266 /**
267 * Initiate connection. Initiation of outgoing connections is not
268 * supported for SAP server.
Jack Hea355e5e2017-08-22 16:06:54 -0700269 *
Andre Eisenbachab258132015-05-04 13:28:04 -0700270 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100271 */
272 public boolean connect(BluetoothDevice device) {
273 if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
274 return false;
275 }
276
277 /**
278 * Initiate disconnect.
279 *
280 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700281 * @return false on error, true otherwise
Andre Eisenbachab258132015-05-04 13:28:04 -0700282 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100283 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100284 @UnsupportedAppUsage
Casper Bonde2a5f6082015-03-19 10:36:45 +0100285 public boolean disconnect(BluetoothDevice device) {
286 if (DBG) log("disconnect(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700287 final IBluetoothSap service = mService;
288 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100289 try {
Jack He16eeac32017-08-17 12:11:18 -0700290 return service.disconnect(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100291 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700292 Log.e(TAG, Log.getStackTraceString(new Throwable()));
293 return false;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100294 }
295 }
Jack He16eeac32017-08-17 12:11:18 -0700296 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100297 return false;
298 }
299
300 /**
301 * Get the list of connected devices. Currently at most one.
302 *
303 * @return list of connected devices
Andre Eisenbachab258132015-05-04 13:28:04 -0700304 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100305 */
306 public List<BluetoothDevice> getConnectedDevices() {
307 if (DBG) log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700308 final IBluetoothSap service = mService;
309 if (service != null && isEnabled()) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100310 try {
Jack He16eeac32017-08-17 12:11:18 -0700311 return service.getConnectedDevices();
Casper Bonde2a5f6082015-03-19 10:36:45 +0100312 } catch (RemoteException e) {
313 Log.e(TAG, Log.getStackTraceString(new Throwable()));
314 return new ArrayList<BluetoothDevice>();
315 }
316 }
Jack He16eeac32017-08-17 12:11:18 -0700317 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100318 return new ArrayList<BluetoothDevice>();
319 }
320
321 /**
322 * Get the list of devices matching specified states. Currently at most one.
323 *
324 * @return list of matching devices
Andre Eisenbachab258132015-05-04 13:28:04 -0700325 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100326 */
327 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
328 if (DBG) log("getDevicesMatchingStates()");
Jack He16eeac32017-08-17 12:11:18 -0700329 final IBluetoothSap service = mService;
330 if (service != null && isEnabled()) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100331 try {
Jack He16eeac32017-08-17 12:11:18 -0700332 return service.getDevicesMatchingConnectionStates(states);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100333 } catch (RemoteException e) {
334 Log.e(TAG, Log.getStackTraceString(new Throwable()));
335 return new ArrayList<BluetoothDevice>();
336 }
337 }
Jack He16eeac32017-08-17 12:11:18 -0700338 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100339 return new ArrayList<BluetoothDevice>();
340 }
341
342 /**
343 * Get connection state of device
344 *
345 * @return device connection state
Andre Eisenbachab258132015-05-04 13:28:04 -0700346 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100347 */
348 public int getConnectionState(BluetoothDevice device) {
349 if (DBG) log("getConnectionState(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700350 final IBluetoothSap service = mService;
351 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100352 try {
Jack He16eeac32017-08-17 12:11:18 -0700353 return service.getConnectionState(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100354 } catch (RemoteException e) {
355 Log.e(TAG, Log.getStackTraceString(new Throwable()));
356 return BluetoothProfile.STATE_DISCONNECTED;
357 }
358 }
Jack He16eeac32017-08-17 12:11:18 -0700359 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100360 return BluetoothProfile.STATE_DISCONNECTED;
361 }
362
363 /**
364 * Set priority of the profile
365 *
366 * <p> The device should already be paired.
Casper Bonde2a5f6082015-03-19 10:36:45 +0100367 *
368 * @param device Paired bluetooth device
369 * @param priority
370 * @return true if priority is set, false on error
Andre Eisenbachab258132015-05-04 13:28:04 -0700371 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100372 */
373 public boolean setPriority(BluetoothDevice device, int priority) {
374 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Jack He16eeac32017-08-17 12:11:18 -0700375 final IBluetoothSap service = mService;
376 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700377 if (priority != BluetoothProfile.PRIORITY_OFF
378 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700379 return false;
Casper Bonde2a5f6082015-03-19 10:36:45 +0100380 }
381 try {
Jack He16eeac32017-08-17 12:11:18 -0700382 return service.setPriority(device, priority);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100383 } catch (RemoteException e) {
384 Log.e(TAG, Log.getStackTraceString(new Throwable()));
385 return false;
386 }
387 }
Jack He16eeac32017-08-17 12:11:18 -0700388 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100389 return false;
390 }
391
392 /**
393 * Get the priority of the profile.
394 *
Casper Bonde2a5f6082015-03-19 10:36:45 +0100395 * @param device Bluetooth device
396 * @return priority of the device
Andre Eisenbachab258132015-05-04 13:28:04 -0700397 * @hide
Casper Bonde2a5f6082015-03-19 10:36:45 +0100398 */
399 public int getPriority(BluetoothDevice device) {
400 if (VDBG) log("getPriority(" + device + ")");
Jack He16eeac32017-08-17 12:11:18 -0700401 final IBluetoothSap service = mService;
402 if (service != null && isEnabled() && isValidDevice(device)) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100403 try {
Jack He16eeac32017-08-17 12:11:18 -0700404 return service.getPriority(device);
Casper Bonde2a5f6082015-03-19 10:36:45 +0100405 } catch (RemoteException e) {
406 Log.e(TAG, Log.getStackTraceString(new Throwable()));
407 return PRIORITY_OFF;
408 }
409 }
Jack He16eeac32017-08-17 12:11:18 -0700410 if (service == null) Log.w(TAG, "Proxy not attached to service");
Casper Bonde2a5f6082015-03-19 10:36:45 +0100411 return PRIORITY_OFF;
412 }
413
Jack He16eeac32017-08-17 12:11:18 -0700414 private final ServiceConnection mConnection = new ServiceConnection() {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100415 public void onServiceConnected(ComponentName className, IBinder service) {
416 if (DBG) log("Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600417 mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
Casper Bonde2a5f6082015-03-19 10:36:45 +0100418 if (mServiceListener != null) {
419 mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
420 }
421 }
Jack Hea355e5e2017-08-22 16:06:54 -0700422
Casper Bonde2a5f6082015-03-19 10:36:45 +0100423 public void onServiceDisconnected(ComponentName className) {
424 if (DBG) log("Proxy object disconnected");
425 mService = null;
426 if (mServiceListener != null) {
427 mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
428 }
429 }
430 };
431
432 private static void log(String msg) {
433 Log.d(TAG, msg);
434 }
435
436 private boolean isEnabled() {
437 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
438
Jack Hea355e5e2017-08-22 16:06:54 -0700439 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
Casper Bonde2a5f6082015-03-19 10:36:45 +0100440 return true;
Jack Hea355e5e2017-08-22 16:06:54 -0700441 }
Casper Bonde2a5f6082015-03-19 10:36:45 +0100442 log("Bluetooth is Not enabled");
443 return false;
444 }
445
Jack He16eeac32017-08-17 12:11:18 -0700446 private static boolean isValidDevice(BluetoothDevice device) {
447 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Casper Bonde2a5f6082015-03-19 10:36:45 +0100448 }
449
450}