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