BluetoothManagerService.java revision 0f42037eb7b5118015c2caca635538324ccf0ccf
1/* 2 * Copyright (C) 2012 Google Inc. 3 */ 4 5package com.android.server; 6 7import android.bluetooth.BluetoothAdapter; 8import android.bluetooth.IBluetooth; 9import android.bluetooth.IBluetoothManager; 10import android.bluetooth.IBluetoothManagerCallback; 11import android.bluetooth.IBluetoothStateChangeCallback; 12 13import android.content.BroadcastReceiver; 14import android.content.ComponentName; 15import android.content.ContentResolver; 16import android.content.Context; 17import android.content.Intent; 18import android.content.IntentFilter; 19import android.content.ServiceConnection; 20import android.os.Handler; 21 22import android.os.IBinder; 23import android.os.Message; 24import android.os.RemoteException; 25import android.provider.Settings; 26import android.util.Log; 27import java.util.List; 28import java.util.ArrayList; 29 30class BluetoothManagerService extends IBluetoothManager.Stub { 31 private static final String TAG = "BluetoothManagerService"; 32 private static final boolean DBG = true; 33 34 private static final boolean ALWAYS_SYNC_NAME_ADDRESS=true; //If true, always load name and address 35 36 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 37 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 38 39 private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED"; 40 private static final String EXTRA_ACTION="action"; 41 42 private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; 43 private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; 44 45 private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind 46 private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save 47 48 private static final int MESSAGE_ENABLE = 1; 49 private static final int MESSAGE_DISABLE = 2; 50 private static final int MESSAGE_REGISTER_ADAPTER = 3; 51 private static final int MESSAGE_UNREGISTER_ADAPTER = 4; 52 private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 5; 53 private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 6; 54 private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 11; 55 private static final int MESSAGE_BLUETOOTH_ON = 12; 56 private static final int MESSAGE_BLUETOOTH_OFF = 14; 57 private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 15; 58 private static final int MESSAGE_TIMEOUT_BIND =100; 59 private static final int MESSAGE_TIMEOUT_UNBIND =101; 60 private static final int MESSAGE_GET_NAME_AND_ADDRESS=200; 61 private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201; 62 private static final int MAX_SAVE_RETRIES=3; 63 64 private final Context mContext; 65 private String mAddress; 66 private String mName; 67 private ContentResolver mContentResolver; 68 private List<IBluetoothManagerCallback> mCallbacks; 69 private List<IBluetoothStateChangeCallback> mStateChangeCallbacks; 70 71 IntentFilter mFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); 72 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 73 74 @Override 75 public void onReceive(Context context, Intent intent) { 76 String action = intent.getAction(); 77 if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { 78 int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); 79 if (state == BluetoothAdapter.STATE_OFF) { 80 Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_OFF); 81 mHandler.sendMessage(msg); 82 } else if (state == BluetoothAdapter.STATE_ON) { 83 Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_ON); 84 mHandler.sendMessage(msg); 85 } 86 } else if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { 87 String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); 88 Log.d(TAG, "Bluetooth Adapter name changed to " + newName); 89 if (newName != null) { 90 storeNameAndAddress(newName, null); 91 } 92 } 93 } 94 }; 95 96 BluetoothManagerService(Context context) { 97 mContext = context; 98 mBluetooth = null; 99 mBinding = false; 100 mUnbinding = false; 101 mAddress = null; 102 mName = null; 103 mContentResolver = context.getContentResolver(); 104 mCallbacks = new ArrayList<IBluetoothManagerCallback>(); 105 mStateChangeCallbacks = new ArrayList<IBluetoothStateChangeCallback>(); 106 mContext.registerReceiver(mReceiver, mFilter); 107 108 int airplaneModeOn = Settings.System.getInt(mContentResolver, 109 Settings.System.AIRPLANE_MODE_ON, 0); 110 int bluetoothOn = Settings.Secure.getInt(mContentResolver, 111 Settings.Secure.BLUETOOTH_ON, 0); 112 if (DBG) Log.d(TAG, "airplane mode: " + airplaneModeOn + " bluetoothOn: " + bluetoothOn); 113 114 loadStoredNameAndAddress(); 115 if (airplaneModeOn == 0 && bluetoothOn!= 0) { 116 //Enable 117 if (DBG) Log.d(TAG, "Autoenabling Bluetooth."); 118 enable(); 119 } else if (ALWAYS_SYNC_NAME_ADDRESS || !isNameAndAddressSet()) { 120 if (DBG) Log.d(TAG,"Retrieving name and address..."); 121 getNameAndAddress(); 122 } 123 } 124 125 private boolean isNameAndAddressSet() { 126 return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0; 127 } 128 129 private void loadStoredNameAndAddress() { 130 if (DBG) Log.d(TAG, "Loading stored name and address"); 131 mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); 132 mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); 133 if (mName == null || mAddress == null) { 134 if (DBG) Log.d(TAG, "Name or address not cached..."); 135 } 136 } 137 138 private void storeNameAndAddress(String name, String address) { 139 if (name != null) { 140 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); 141 if (DBG) Log.d(TAG,"Stored name: " + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME)); 142 mName = name; 143 } 144 145 if (address != null) { 146 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); 147 if (DBG) Log.d(TAG,"Stored address: " + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS)); 148 mAddress=address; 149 } 150 } 151 152 public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ 153 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 154 "Need BLUETOOTH permission"); 155 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); 156 msg.obj = callback; 157 mHandler.sendMessage(msg); 158 synchronized(mConnection) { 159 return mBluetooth; 160 } 161 } 162 163 public void unregisterAdapter(IBluetoothManagerCallback callback) { 164 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 165 "Need BLUETOOTH permission"); 166 Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); 167 msg.obj = callback; 168 mHandler.sendMessage(msg); 169 } 170 171 public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { 172 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 173 "Need BLUETOOTH permission"); 174 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); 175 msg.obj = callback; 176 mHandler.sendMessage(msg); 177 } 178 179 public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { 180 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 181 "Need BLUETOOTH permission"); 182 Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); 183 msg.obj = callback; 184 mHandler.sendMessage(msg); 185 } 186 187 public boolean isEnabled() { 188 synchronized(mConnection) { 189 try { 190 return (mBluetooth != null && mBluetooth.isEnabled()); 191 } catch (RemoteException e) { 192 Log.e(TAG, "isEnabled()", e); 193 } 194 } 195 return false; 196 } 197 198 public void getNameAndAddress() { 199 synchronized(mConnection) { 200 if (mBinding) return ; 201 mBinding = true; 202 } 203 Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); 204 mHandler.sendMessage(msg); 205 } 206 207 public boolean enable() { 208 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 209 "Need BLUETOOTH ADMIN permission"); 210 synchronized(mConnection) { 211 //if (mBluetooth != null) return false; [fc] always allow an enable() to occur. 212 //If service is bound, we should not assume that bluetooth is enabled. What if 213 //Bluetooth never turned on? 214 if (mBinding) return true; 215 mBinding = true; 216 } 217 Message msg = mHandler.obtainMessage(MESSAGE_ENABLE); 218 //msg.obj = new Boolean(true); 219 mHandler.sendMessage(msg); 220 return true; 221 } 222 223 public boolean disable(boolean persist) { 224 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 225 "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); 226 synchronized(mConnection) { 227 if (mBluetooth == null) return false; 228 //if (mUnbinding) return true; 229 //mUnbinding = true; 230 } 231 Message msg = mHandler.obtainMessage(MESSAGE_DISABLE); 232 msg.obj = new Boolean(persist); 233 mHandler.sendMessage(msg); 234 return true; 235 } 236 237 public void unbindAndFinish(boolean sendStop) { 238 synchronized (mConnection) { 239 if (mUnbinding) return; 240 mUnbinding = true; 241 if (mIsConnected) { 242 if (sendStop) { 243 if (DBG) Log.d(TAG,"Sending stop request."); 244 Intent i = new Intent(IBluetooth.class.getName()); 245 i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED); 246 i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_OFF); 247 mContext.startService(i); 248 } 249 if (DBG) Log.d(TAG, "Sending unbind request."); 250 mContext.unbindService(mConnection); 251 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED)); 252 } 253 } 254 } 255 256 public String getAddress() { 257 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 258 "Need BLUETOOTH ADMIN permission"); 259 return mAddress; 260 } 261 public String getName() { 262 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 263 "Need BLUETOOTH ADMIN permission"); 264 return mName; 265 } 266 267 private IBluetooth mBluetooth; 268 private boolean mBinding; 269 private boolean mUnbinding; 270 public boolean mIsConnected; 271 272 private class BluetoothServiceConnection implements ServiceConnection { 273 274 private boolean mGetNameAddressOnly; 275 276 public void setGetNameAddressOnly(boolean getOnly) { 277 mGetNameAddressOnly = getOnly; 278 } 279 280 public boolean isGetNameAddressOnly() { 281 return mGetNameAddressOnly; 282 } 283 284 public void onServiceConnected(ComponentName className, IBinder service) { 285 if (DBG) Log.d(TAG, "Proxy object connected"); 286 Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); 287 msg.obj = service; 288 mHandler.sendMessage(msg); 289 } 290 291 public void onServiceDisconnected(ComponentName className) { 292 if (DBG) Log.d(TAG, "Proxy object disconnected"); 293 // Called if we unexpected disconnected. 294 Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); 295 mHandler.sendMessage(msg); 296 } 297 } 298 299 private BluetoothServiceConnection mConnection = new BluetoothServiceConnection(); 300 301 private final Handler mHandler = new Handler() { 302 @Override 303 public void handleMessage(Message msg) { 304 if (DBG) Log.d (TAG, "Message: " + msg.what); 305 306 switch (msg.what) { 307 case MESSAGE_GET_NAME_AND_ADDRESS: { 308 if (mBluetooth == null) { 309 //Start bind request 310 if (!mIsConnected) { 311 if (DBG) Log.d(TAG, "Binding to service to get name and address"); 312 mConnection.setGetNameAddressOnly(true); 313 //Start bind timeout and bind 314 Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); 315 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); 316 Intent i = new Intent(IBluetooth.class.getName()); 317 if (!mContext.bindService(i, mConnection, 318 Context.BIND_AUTO_CREATE)) { 319 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); 320 Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName()); 321 } 322 } 323 } else { 324 Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS); 325 mHandler.sendMessage(saveMsg); 326 } 327 } 328 break; 329 case MESSAGE_SAVE_NAME_AND_ADDRESS: { 330 if (mBluetooth != null) { 331 String name = null; 332 String address = null; 333 try { 334 name = mBluetooth.getName(); 335 address = mBluetooth.getAddress(); 336 } catch (RemoteException re) { 337 Log.e(TAG,"",re); 338 } 339 340 if (name != null && address != null) { 341 storeNameAndAddress(name,address); 342 unbindAndFinish(false); 343 } else { 344 if (msg.arg1 < MAX_SAVE_RETRIES) { 345 Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS); 346 retryMsg.arg1= 1+msg.arg1; 347 if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1); 348 mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS); 349 } else { 350 Log.w(TAG,"Maximum name/address remote retrieval retry exceeded"); 351 unbindAndFinish(false); 352 } 353 } 354 } 355 } 356 break; 357 case MESSAGE_ENABLE: { 358 if (mBluetooth == null) { 359 //Start bind request 360 if (!mIsConnected) { 361 //Start bind timeout and bind 362 Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); 363 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); 364 Intent i = new Intent(IBluetooth.class.getName()); 365 i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED); 366 i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_ON); 367 mContext.startService(i); 368 mConnection.setGetNameAddressOnly(false); 369 if (!mContext.bindService(i, mConnection, 370 Context.BIND_AUTO_CREATE)) { 371 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); 372 Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName()); 373 } 374 } 375 } else { 376 //Check if name and address is loaded if not get it first. 377 if (ALWAYS_SYNC_NAME_ADDRESS || !isNameAndAddressSet()) { 378 try { 379 if (DBG) Log.d(TAG,"Bluetooth Proxy available: getting name and address prior to enable."); 380 storeNameAndAddress(mBluetooth.getName(),mBluetooth.getAddress()); 381 } catch (RemoteException e) {Log.e(TAG, "", e);}; 382 } 383 try { 384 mBluetooth.enable(); 385 } catch (RemoteException e) {Log.e(TAG, "", e);}; 386 } 387 // TODO(BT) what if service failed to start: 388 // [fc] fixed: watch for bind timeout and handle accordingly 389 // TODO(BT) persist the setting depending on argument 390 // [fc]: let AdapterServiceHandle 391 } 392 break; 393 case MESSAGE_DISABLE: 394 if (mBluetooth != null ) { 395 boolean persist = (Boolean)msg.obj; 396 try { 397 mConnection.setGetNameAddressOnly(false); 398 mBluetooth.disable(persist); 399 //We will only unbind once we are sure that Bluetooth is OFFMESSAGE_TIMEOUT_UNBIND 400 //mContext.unbindService(mConnection); 401 } catch (RemoteException e) { 402 Log.e(TAG, "Error disabling Bluetooth", e); 403 } 404 } 405 406 // TODO(BT) what if service failed to stop: 407 // [fc] fixed: watch for disable event and unbind accordingly 408 // TODO(BT) persist the setting depending on argument 409 // [fc]: let AdapterServiceHandle 410 411 break; 412 case MESSAGE_REGISTER_ADAPTER: 413 { 414 IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; 415 mCallbacks.add(callback); 416 } 417 break; 418 case MESSAGE_UNREGISTER_ADAPTER: 419 { 420 IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; 421 mCallbacks.remove(callback); 422 } 423 break; 424 case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: 425 { 426 IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; 427 mStateChangeCallbacks.add(callback); 428 } 429 break; 430 case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: 431 { 432 IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; 433 mStateChangeCallbacks.remove(callback); 434 } 435 break; 436 case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: 437 { 438 if (DBG) Log.d(TAG,"Bluetooth service connnected!"); 439 //Remove timeout 440 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); 441 442 IBinder service = (IBinder) msg.obj; 443 synchronized(mConnection) { 444 mIsConnected=true; 445 mBinding = false; 446 mBluetooth = IBluetooth.Stub.asInterface(service); 447 } 448 449 if (mConnection.isGetNameAddressOnly()) { 450 //Request GET NAME AND ADDRESS 451 Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); 452 mHandler.sendMessage(getMsg); 453 return; 454 } 455 456 //Otherwise do the enable 457 if (DBG) Log.d(TAG,"Requesting Bluetooth enable..."); 458 try { 459 for (IBluetoothManagerCallback callback : mCallbacks) { 460 callback.onBluetoothServiceUp(mBluetooth); 461 } 462 } catch (RemoteException e) { 463 Log.e(TAG, "", e); 464 } 465 466 //Request Enable 467 Message enableMsg = mHandler.obtainMessage(MESSAGE_ENABLE); 468 //enableMsg.obj = new Boolean(false); 469 mHandler.sendMessage(enableMsg); 470 } 471 break; 472 case MESSAGE_TIMEOUT_BIND: 473 { 474 Log.e(TAG, "Timeout while trying to bind to Bluetooth Service"); 475 synchronized(mConnection) { 476 mBinding = false; 477 } 478 } 479 break; 480 481 case MESSAGE_BLUETOOTH_ON: 482 { 483 if (DBG) Log.d(TAG, "Bluetooth is on!!!"); 484 try { 485 for (IBluetoothStateChangeCallback callback : mStateChangeCallbacks) { 486 callback.onBluetoothStateChange(true); 487 } 488 } catch (RemoteException e) { 489 Log.e(TAG, "", e); 490 } 491 } 492 break; 493 494 case MESSAGE_BLUETOOTH_OFF: 495 { 496 if (DBG) Log.d(TAG, "Bluetooth is off. Unbinding..."); 497 498 try { 499 for (IBluetoothStateChangeCallback callback : mStateChangeCallbacks) { 500 callback.onBluetoothStateChange(false); 501 } 502 } catch (RemoteException e) { 503 Log.e(TAG, "", e); 504 } 505 unbindAndFinish(true); 506 } 507 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: 508 { 509 boolean isUnexpectedDisconnect = false; 510 synchronized(mConnection) { 511 mBluetooth = null; 512 mIsConnected=false; 513 if (mUnbinding) { 514 mUnbinding = false; 515 } else { 516 isUnexpectedDisconnect = true; 517 } 518 } 519 if (!isUnexpectedDisconnect &&!mConnection.isGetNameAddressOnly()) { 520 if (DBG) Log.d(TAG,"Service finished unbinding. Calling callbacks..."); 521 try { 522 for (IBluetoothManagerCallback callback : mCallbacks) { 523 callback.onBluetoothServiceDown(); 524 } 525 } catch (RemoteException e) { 526 Log.e(TAG, "", e); 527 } 528 } 529 } 530 break; 531 case MESSAGE_TIMEOUT_UNBIND: 532 { 533 Log.e(TAG, "Timeout while trying to unbind to Bluetooth Service"); 534 synchronized(mConnection) { 535 mUnbinding = false; 536 } 537 } 538 break; 539 } 540 } 541 }; 542} 543