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.annotation.SystemApi; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.os.Binder; 25 import android.os.IBinder; 26 import android.os.RemoteException; 27 import android.util.Log; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 32 /** 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. 41 * 42 * @hide 43 */ 44 public 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 50 /** 51 * Intent used to broadcast the change in connection state of the profile. 52 * 53 * <p>This intent will have 4 extras: 54 * <ul> 55 * <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> 58 * </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. 66 * 67 * @hide 68 */ 69 public static final String ACTION_CONNECTION_STATE_CHANGED = 70 "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; 71 72 /** 73 * There was an error trying to obtain the state. 74 * 75 * @hide 76 */ 77 public static final int STATE_ERROR = -1; 78 79 /** 80 * Connection state change succceeded. 81 * 82 * @hide 83 */ 84 public static final int RESULT_SUCCESS = 1; 85 86 /** 87 * Connection canceled before completion. 88 * 89 * @hide 90 */ 91 public static final int RESULT_CANCELED = 2; 92 93 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)); 100 } 101 }; 102 103 /** 104 * Create a BluetoothSap proxy object. 105 */ BluetoothSap(Context context, ServiceListener listener)106 /*package*/ BluetoothSap(Context context, ServiceListener listener) { 107 if (DBG) Log.d(TAG, "Create BluetoothSap proxy object"); 108 mAdapter = BluetoothAdapter.getDefaultAdapter(); 109 mProfileConnector.connect(context, listener); 110 } 111 finalize()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. 125 * 126 * @hide 127 */ close()128 public synchronized void close() { 129 mProfileConnector.disconnect(); 130 } 131 getService()132 private IBluetoothSap getService() { 133 return mProfileConnector.getService(); 134 } 135 136 /** 137 * Get the current state of the BluetoothSap service. 138 * 139 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not 140 * connected to the Sap service. 141 * @hide 142 */ getState()143 public int getState() { 144 if (VDBG) log("getState()"); 145 final IBluetoothSap service = getService(); 146 if (service != null) { 147 try { 148 return service.getState(); 149 } catch (RemoteException e) { 150 Log.e(TAG, e.toString()); 151 } 152 } 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). 161 * 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. 164 * @hide 165 */ getClient()166 public BluetoothDevice getClient() { 167 if (VDBG) log("getClient()"); 168 final IBluetoothSap service = getService(); 169 if (service != null) { 170 try { 171 return service.getClient(); 172 } catch (RemoteException e) { 173 Log.e(TAG, e.toString()); 174 } 175 } 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. 186 * 187 * @hide 188 */ isConnected(BluetoothDevice device)189 public boolean isConnected(BluetoothDevice device) { 190 if (VDBG) log("isConnected(" + device + ")"); 191 final IBluetoothSap service = getService(); 192 if (service != null) { 193 try { 194 return service.isConnected(device); 195 } catch (RemoteException e) { 196 Log.e(TAG, e.toString()); 197 } 198 } 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. 208 * 209 * @hide 210 */ connect(BluetoothDevice device)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 220 * @return false on error, true otherwise 221 * @hide 222 */ 223 @UnsupportedAppUsage disconnect(BluetoothDevice device)224 public boolean disconnect(BluetoothDevice device) { 225 if (DBG) log("disconnect(" + device + ")"); 226 final IBluetoothSap service = getService(); 227 if (service != null && isEnabled() && isValidDevice(device)) { 228 try { 229 return service.disconnect(device); 230 } catch (RemoteException e) { 231 Log.e(TAG, Log.getStackTraceString(new Throwable())); 232 return false; 233 } 234 } 235 if (service == null) Log.w(TAG, "Proxy not attached to service"); 236 return false; 237 } 238 239 /** 240 * Get the list of connected devices. Currently at most one. 241 * 242 * @return list of connected devices 243 * @hide 244 */ getConnectedDevices()245 public List<BluetoothDevice> getConnectedDevices() { 246 if (DBG) log("getConnectedDevices()"); 247 final IBluetoothSap service = getService(); 248 if (service != null && isEnabled()) { 249 try { 250 return service.getConnectedDevices(); 251 } catch (RemoteException e) { 252 Log.e(TAG, Log.getStackTraceString(new Throwable())); 253 return new ArrayList<BluetoothDevice>(); 254 } 255 } 256 if (service == null) Log.w(TAG, "Proxy not attached to service"); 257 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 264 * @hide 265 */ getDevicesMatchingConnectionStates(int[] states)266 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 267 if (DBG) log("getDevicesMatchingStates()"); 268 final IBluetoothSap service = getService(); 269 if (service != null && isEnabled()) { 270 try { 271 return service.getDevicesMatchingConnectionStates(states); 272 } catch (RemoteException e) { 273 Log.e(TAG, Log.getStackTraceString(new Throwable())); 274 return new ArrayList<BluetoothDevice>(); 275 } 276 } 277 if (service == null) Log.w(TAG, "Proxy not attached to service"); 278 return new ArrayList<BluetoothDevice>(); 279 } 280 281 /** 282 * Get connection state of device 283 * 284 * @return device connection state 285 * @hide 286 */ getConnectionState(BluetoothDevice device)287 public int getConnectionState(BluetoothDevice device) { 288 if (DBG) log("getConnectionState(" + device + ")"); 289 final IBluetoothSap service = getService(); 290 if (service != null && isEnabled() && isValidDevice(device)) { 291 try { 292 return service.getConnectionState(device); 293 } catch (RemoteException e) { 294 Log.e(TAG, Log.getStackTraceString(new Throwable())); 295 return BluetoothProfile.STATE_DISCONNECTED; 296 } 297 } 298 if (service == null) Log.w(TAG, "Proxy not attached to service"); 299 return BluetoothProfile.STATE_DISCONNECTED; 300 } 301 302 /** 303 * Set priority of the profile 304 * 305 * <p> The device should already be paired. 306 * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, 307 * 308 * @param device Paired bluetooth device 309 * @param priority 310 * @return true if priority is set, false on error 311 * @hide 312 */ 313 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) setPriority(BluetoothDevice device, int priority)314 public boolean setPriority(BluetoothDevice device, int priority) { 315 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 316 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_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy)333 public boolean setConnectionPolicy(BluetoothDevice device, 334 @ConnectionPolicy int connectionPolicy) { 335 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); 336 final IBluetoothSap service = getService(); 337 if (service != null && isEnabled() && isValidDevice(device)) { 338 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN 339 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 340 return false; 341 } 342 try { 343 return service.setConnectionPolicy(device, connectionPolicy); 344 } catch (RemoteException e) { 345 Log.e(TAG, Log.getStackTraceString(new Throwable())); 346 return false; 347 } 348 } 349 if (service == null) Log.w(TAG, "Proxy not attached to service"); 350 return false; 351 } 352 353 /** 354 * Get the priority of the profile. 355 * 356 * <p> The priority can be any of: 357 * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} 358 * 359 * @param device Bluetooth device 360 * @return priority of the device 361 * @hide 362 */ 363 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) getPriority(BluetoothDevice device)364 public int getPriority(BluetoothDevice device) { 365 if (VDBG) log("getPriority(" + device + ")"); 366 return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); 367 } 368 369 /** 370 * Get the connection policy of the profile. 371 * 372 * <p> The connection policy can be any of: 373 * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, 374 * {@link #CONNECTION_POLICY_UNKNOWN} 375 * 376 * @param device Bluetooth device 377 * @return connection policy of the device 378 * @hide 379 */ 380 @SystemApi 381 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) getConnectionPolicy(BluetoothDevice device)382 public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { 383 if (VDBG) log("getConnectionPolicy(" + device + ")"); 384 final IBluetoothSap service = getService(); 385 if (service != null && isEnabled() && isValidDevice(device)) { 386 try { 387 return service.getConnectionPolicy(device); 388 } catch (RemoteException e) { 389 Log.e(TAG, Log.getStackTraceString(new Throwable())); 390 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 391 } 392 } 393 if (service == null) Log.w(TAG, "Proxy not attached to service"); 394 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 395 } 396 log(String msg)397 private static void log(String msg) { 398 Log.d(TAG, msg); 399 } 400 isEnabled()401 private boolean isEnabled() { 402 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 403 404 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { 405 return true; 406 } 407 log("Bluetooth is Not enabled"); 408 return false; 409 } 410 isValidDevice(BluetoothDevice device)411 private static boolean isValidDevice(BluetoothDevice device) { 412 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); 413 } 414 415 } 416