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