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