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 java.util.ArrayList; 20 import java.util.List; 21 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.ServiceConnection; 26 import android.os.RemoteException; 27 import android.os.IBinder; 28 import android.os.ServiceManager; 29 import android.util.Log; 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 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 */ 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 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 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 */ 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 */ 196 public int getState() { 197 if (VDBG) log("getState()"); 198 if (mService != null) { 199 try { 200 return mService.getState(); 201 } catch (RemoteException e) {Log.e(TAG, e.toString());} 202 } else { 203 Log.w(TAG, "Proxy not attached to service"); 204 if (DBG) log(Log.getStackTraceString(new Throwable())); 205 } 206 return BluetoothSap.STATE_ERROR; 207 } 208 209 /** 210 * Get the currently connected remote Bluetooth device (PCE). 211 * @return The remote Bluetooth device, or null if not in connected or 212 * connecting state, or if this proxy object is not connected to 213 * the Sap service. 214 * @hide 215 */ 216 public BluetoothDevice getClient() { 217 if (VDBG) log("getClient()"); 218 if (mService != null) { 219 try { 220 return mService.getClient(); 221 } catch (RemoteException e) {Log.e(TAG, e.toString());} 222 } else { 223 Log.w(TAG, "Proxy not attached to service"); 224 if (DBG) log(Log.getStackTraceString(new Throwable())); 225 } 226 return null; 227 } 228 229 /** 230 * Returns true if the specified Bluetooth device is connected. 231 * Returns false if not connected, or if this proxy object is not 232 * currently connected to the Sap service. 233 * @hide 234 */ 235 public boolean isConnected(BluetoothDevice device) { 236 if (VDBG) log("isConnected(" + device + ")"); 237 if (mService != null) { 238 try { 239 return mService.isConnected(device); 240 } catch (RemoteException e) {Log.e(TAG, e.toString());} 241 } else { 242 Log.w(TAG, "Proxy not attached to service"); 243 if (DBG) log(Log.getStackTraceString(new Throwable())); 244 } 245 return false; 246 } 247 248 /** 249 * Initiate connection. Initiation of outgoing connections is not 250 * supported for SAP server. 251 * @hide 252 */ 253 public boolean connect(BluetoothDevice device) { 254 if (DBG) log("connect(" + device + ")" + "not supported for SAPS"); 255 return false; 256 } 257 258 /** 259 * Initiate disconnect. 260 * 261 * @param device Remote Bluetooth Device 262 * @return false on error, 263 * true otherwise 264 * @hide 265 */ 266 public boolean disconnect(BluetoothDevice device) { 267 if (DBG) log("disconnect(" + device + ")"); 268 if (mService != null && isEnabled() && 269 isValidDevice(device)) { 270 try { 271 return mService.disconnect(device); 272 } catch (RemoteException e) { 273 Log.e(TAG, Log.getStackTraceString(new Throwable())); 274 return false; 275 } 276 } 277 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 278 return false; 279 } 280 281 /** 282 * Get the list of connected devices. Currently at most one. 283 * 284 * @return list of connected devices 285 * @hide 286 */ 287 public List<BluetoothDevice> getConnectedDevices() { 288 if (DBG) log("getConnectedDevices()"); 289 if (mService != null && isEnabled()) { 290 try { 291 return mService.getConnectedDevices(); 292 } catch (RemoteException e) { 293 Log.e(TAG, Log.getStackTraceString(new Throwable())); 294 return new ArrayList<BluetoothDevice>(); 295 } 296 } 297 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 298 return new ArrayList<BluetoothDevice>(); 299 } 300 301 /** 302 * Get the list of devices matching specified states. Currently at most one. 303 * 304 * @return list of matching devices 305 * @hide 306 */ 307 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 308 if (DBG) log("getDevicesMatchingStates()"); 309 if (mService != null && isEnabled()) { 310 try { 311 return mService.getDevicesMatchingConnectionStates(states); 312 } catch (RemoteException e) { 313 Log.e(TAG, Log.getStackTraceString(new Throwable())); 314 return new ArrayList<BluetoothDevice>(); 315 } 316 } 317 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 318 return new ArrayList<BluetoothDevice>(); 319 } 320 321 /** 322 * Get connection state of device 323 * 324 * @return device connection state 325 * @hide 326 */ 327 public int getConnectionState(BluetoothDevice device) { 328 if (DBG) log("getConnectionState(" + device + ")"); 329 if (mService != null && isEnabled() && 330 isValidDevice(device)) { 331 try { 332 return mService.getConnectionState(device); 333 } catch (RemoteException e) { 334 Log.e(TAG, Log.getStackTraceString(new Throwable())); 335 return BluetoothProfile.STATE_DISCONNECTED; 336 } 337 } 338 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 339 return BluetoothProfile.STATE_DISCONNECTED; 340 } 341 342 /** 343 * Set priority of the profile 344 * 345 * <p> The device should already be paired. 346 * 347 * @param device Paired bluetooth device 348 * @param priority 349 * @return true if priority is set, false on error 350 * @hide 351 */ 352 public boolean setPriority(BluetoothDevice device, int priority) { 353 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 354 if (mService != null && isEnabled() && 355 isValidDevice(device)) { 356 if (priority != BluetoothProfile.PRIORITY_OFF && 357 priority != BluetoothProfile.PRIORITY_ON) { 358 return false; 359 } 360 try { 361 return mService.setPriority(device, priority); 362 } catch (RemoteException e) { 363 Log.e(TAG, Log.getStackTraceString(new Throwable())); 364 return false; 365 } 366 } 367 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 368 return false; 369 } 370 371 /** 372 * Get the priority of the profile. 373 * 374 * @param device Bluetooth device 375 * @return priority of the device 376 * @hide 377 */ 378 public int getPriority(BluetoothDevice device) { 379 if (VDBG) log("getPriority(" + device + ")"); 380 if (mService != null && isEnabled() && 381 isValidDevice(device)) { 382 try { 383 return mService.getPriority(device); 384 } catch (RemoteException e) { 385 Log.e(TAG, Log.getStackTraceString(new Throwable())); 386 return PRIORITY_OFF; 387 } 388 } 389 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 390 return PRIORITY_OFF; 391 } 392 393 private ServiceConnection mConnection = new ServiceConnection() { 394 public void onServiceConnected(ComponentName className, IBinder service) { 395 if (DBG) log("Proxy object connected"); 396 mService = IBluetoothSap.Stub.asInterface(service); 397 if (mServiceListener != null) { 398 mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); 399 } 400 } 401 public void onServiceDisconnected(ComponentName className) { 402 if (DBG) log("Proxy object disconnected"); 403 mService = null; 404 if (mServiceListener != null) { 405 mServiceListener.onServiceDisconnected(BluetoothProfile.SAP); 406 } 407 } 408 }; 409 410 private static void log(String msg) { 411 Log.d(TAG, msg); 412 } 413 414 private boolean isEnabled() { 415 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 416 417 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) 418 return true; 419 log("Bluetooth is Not enabled"); 420 return false; 421 } 422 423 private boolean isValidDevice(BluetoothDevice device) { 424 if (device == null) 425 return false; 426 427 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) 428 return true; 429 return false; 430 } 431 432 } 433