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