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