1 /* 2 * Copyright (C) 2012 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 com.android.server; 18 19 import android.app.ActivityManager; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.IBluetooth; 22 import android.bluetooth.IBluetoothGatt; 23 import android.bluetooth.IBluetoothCallback; 24 import android.bluetooth.IBluetoothManager; 25 import android.bluetooth.IBluetoothManagerCallback; 26 import android.bluetooth.IBluetoothStateChangeCallback; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.ContentResolver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.ServiceConnection; 34 import android.content.pm.PackageManager; 35 import android.os.Binder; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.IBinder; 39 import android.os.Looper; 40 import android.os.Message; 41 import android.os.Process; 42 import android.os.RemoteCallbackList; 43 import android.os.RemoteException; 44 import android.os.SystemClock; 45 import android.os.UserHandle; 46 import android.provider.Settings; 47 import android.util.Log; 48 class BluetoothManagerService extends IBluetoothManager.Stub { 49 private static final String TAG = "BluetoothManagerService"; 50 private static final boolean DBG = true; 51 52 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 53 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 54 private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED"; 55 private static final String EXTRA_ACTION="action"; 56 private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; 57 private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; 58 private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; 59 private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind 60 private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save 61 //Maximum msec to wait for service restart 62 private static final int SERVICE_RESTART_TIME_MS = 200; 63 //Maximum msec to wait for restart due to error 64 private static final int ERROR_RESTART_TIME_MS = 3000; 65 //Maximum msec to delay MESSAGE_USER_SWITCHED 66 private static final int USER_SWITCHED_TIME_MS = 200; 67 68 private static final int MESSAGE_ENABLE = 1; 69 private static final int MESSAGE_DISABLE = 2; 70 private static final int MESSAGE_REGISTER_ADAPTER = 20; 71 private static final int MESSAGE_UNREGISTER_ADAPTER = 21; 72 private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; 73 private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31; 74 private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; 75 private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41; 76 private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42; 77 private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60; 78 private static final int MESSAGE_TIMEOUT_BIND =100; 79 private static final int MESSAGE_TIMEOUT_UNBIND =101; 80 private static final int MESSAGE_GET_NAME_AND_ADDRESS=200; 81 private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201; 82 private static final int MESSAGE_USER_SWITCHED = 300; 83 private static final int MAX_SAVE_RETRIES=3; 84 private static final int MAX_ERROR_RESTART_RETRIES=6; 85 86 // Bluetooth persisted setting is off 87 private static final int BLUETOOTH_OFF=0; 88 // Bluetooth persisted setting is on 89 // and Airplane mode won't affect Bluetooth state at start up 90 private static final int BLUETOOTH_ON_BLUETOOTH=1; 91 // Bluetooth persisted setting is on 92 // but Airplane mode will affect Bluetooth state at start up 93 // and Airplane mode will have higher priority. 94 private static final int BLUETOOTH_ON_AIRPLANE=2; 95 96 private static final int SERVICE_IBLUETOOTH = 1; 97 private static final int SERVICE_IBLUETOOTHGATT = 2; 98 99 private final Context mContext; 100 101 // Locks are not provided for mName and mAddress. 102 // They are accessed in handler or broadcast receiver, same thread context. 103 private String mAddress; 104 private String mName; 105 private final ContentResolver mContentResolver; 106 private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks; 107 private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks; 108 private IBluetooth mBluetooth; 109 private IBluetoothGatt mBluetoothGatt; 110 private boolean mBinding; 111 private boolean mUnbinding; 112 // used inside handler thread 113 private boolean mQuietEnable = false; 114 // configuarion from external IBinder call which is used to 115 // synchronize with broadcast receiver. 116 private boolean mQuietEnableExternal; 117 // configuarion from external IBinder call which is used to 118 // synchronize with broadcast receiver. 119 private boolean mEnableExternal; 120 // used inside handler thread 121 private boolean mEnable; 122 private int mState; 123 private HandlerThread mThread; 124 private final BluetoothHandler mHandler; 125 private int mErrorRecoveryRetryCounter; 126 127 private void registerForAirplaneMode(IntentFilter filter) { 128 final ContentResolver resolver = mContext.getContentResolver(); 129 final String airplaneModeRadios = Settings.Global.getString(resolver, 130 Settings.Global.AIRPLANE_MODE_RADIOS); 131 final String toggleableRadios = Settings.Global.getString(resolver, 132 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 133 boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true : 134 airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH); 135 if (mIsAirplaneSensitive) { 136 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 137 } 138 } 139 140 private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { 141 @Override 142 public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { 143 Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState); 144 mHandler.sendMessage(msg); 145 } 146 }; 147 148 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 149 @Override 150 public void onReceive(Context context, Intent intent) { 151 String action = intent.getAction(); 152 if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { 153 String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); 154 if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName); 155 if (newName != null) { 156 storeNameAndAddress(newName, null); 157 } 158 } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { 159 synchronized(mReceiver) { 160 if (isBluetoothPersistedStateOn()) { 161 if (isAirplaneModeOn()) { 162 persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); 163 } else { 164 persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); 165 } 166 } 167 if (isAirplaneModeOn()) { 168 // disable without persisting the setting 169 sendDisableMsg(); 170 } else if (mEnableExternal) { 171 // enable without persisting the setting 172 sendEnableMsg(mQuietEnableExternal); 173 } 174 } 175 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { 176 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, 177 intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); 178 } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { 179 synchronized(mReceiver) { 180 if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { 181 //Enable 182 if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); 183 sendEnableMsg(mQuietEnableExternal); 184 } 185 } 186 187 if (!isNameAndAddressSet()) { 188 //Sync the Bluetooth name and address from the Bluetooth Adapter 189 if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); 190 getNameAndAddress(); 191 } 192 } 193 } 194 }; 195 196 BluetoothManagerService(Context context) { 197 mThread = new HandlerThread("BluetoothManager"); 198 mThread.start(); 199 mHandler = new BluetoothHandler(mThread.getLooper()); 200 201 mContext = context; 202 mBluetooth = null; 203 mBinding = false; 204 mUnbinding = false; 205 mEnable = false; 206 mState = BluetoothAdapter.STATE_OFF; 207 mQuietEnableExternal = false; 208 mEnableExternal = false; 209 mAddress = null; 210 mName = null; 211 mErrorRecoveryRetryCounter = 0; 212 mContentResolver = context.getContentResolver(); 213 mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>(); 214 mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>(); 215 IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 216 filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); 217 filter.addAction(Intent.ACTION_USER_SWITCHED); 218 registerForAirplaneMode(filter); 219 mContext.registerReceiver(mReceiver, filter); 220 loadStoredNameAndAddress(); 221 if (isBluetoothPersistedStateOn()) { 222 mEnableExternal = true; 223 } 224 } 225 226 /** 227 * Returns true if airplane mode is currently on 228 */ 229 private final boolean isAirplaneModeOn() { 230 return Settings.Global.getInt(mContext.getContentResolver(), 231 Settings.Global.AIRPLANE_MODE_ON, 0) == 1; 232 } 233 234 /** 235 * Returns true if the Bluetooth saved state is "on" 236 */ 237 private final boolean isBluetoothPersistedStateOn() { 238 return Settings.Global.getInt(mContentResolver, 239 Settings.Global.BLUETOOTH_ON, 0) != BLUETOOTH_OFF; 240 } 241 242 /** 243 * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH 244 */ 245 private final boolean isBluetoothPersistedStateOnBluetooth() { 246 return Settings.Global.getInt(mContentResolver, 247 Settings.Global.BLUETOOTH_ON, 0) == BLUETOOTH_ON_BLUETOOTH; 248 } 249 250 /** 251 * Save the Bluetooth on/off state 252 * 253 */ 254 private void persistBluetoothSetting(int value) { 255 Settings.Global.putInt(mContext.getContentResolver(), 256 Settings.Global.BLUETOOTH_ON, 257 value); 258 } 259 260 /** 261 * Returns true if the Bluetooth Adapter's name and address is 262 * locally cached 263 * @return 264 */ 265 private boolean isNameAndAddressSet() { 266 return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0; 267 } 268 269 /** 270 * Retrieve the Bluetooth Adapter's name and address and save it in 271 * in the local cache 272 */ 273 private void loadStoredNameAndAddress() { 274 if (DBG) Log.d(TAG, "Loading stored name and address"); 275 if (mContext.getResources().getBoolean 276 (com.android.internal.R.bool.config_bluetooth_address_validation) && 277 Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) { 278 // if the valid flag is not set, don't load the address and name 279 if (DBG) Log.d(TAG, "invalid bluetooth name and address stored"); 280 return; 281 } 282 mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); 283 mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); 284 if (DBG) Log.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); 285 } 286 287 /** 288 * Save the Bluetooth name and address in the persistent store. 289 * Only non-null values will be saved. 290 * @param name 291 * @param address 292 */ 293 private void storeNameAndAddress(String name, String address) { 294 if (name != null) { 295 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); 296 mName = name; 297 if (DBG) Log.d(TAG,"Stored Bluetooth name: " + 298 Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME)); 299 } 300 301 if (address != null) { 302 Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); 303 mAddress=address; 304 if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " + 305 Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS)); 306 } 307 308 if ((name != null) && (address != null)) { 309 Settings.Secure.putInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1); 310 } 311 } 312 313 public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ 314 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); 315 msg.obj = callback; 316 mHandler.sendMessage(msg); 317 synchronized(mConnection) { 318 return mBluetooth; 319 } 320 } 321 322 public void unregisterAdapter(IBluetoothManagerCallback callback) { 323 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 324 "Need BLUETOOTH permission"); 325 Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); 326 msg.obj = callback; 327 mHandler.sendMessage(msg); 328 } 329 330 public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { 331 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 332 "Need BLUETOOTH permission"); 333 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); 334 msg.obj = callback; 335 mHandler.sendMessage(msg); 336 } 337 338 public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { 339 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 340 "Need BLUETOOTH permission"); 341 Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); 342 msg.obj = callback; 343 mHandler.sendMessage(msg); 344 } 345 346 public boolean isEnabled() { 347 if ((Binder.getCallingUid() != Process.SYSTEM_UID) && 348 (!checkIfCallerIsForegroundUser())) { 349 Log.w(TAG,"isEnabled(): not allowed for non-active and non system user"); 350 return false; 351 } 352 353 synchronized(mConnection) { 354 try { 355 return (mBluetooth != null && mBluetooth.isEnabled()); 356 } catch (RemoteException e) { 357 Log.e(TAG, "isEnabled()", e); 358 } 359 } 360 return false; 361 } 362 363 public void getNameAndAddress() { 364 if (DBG) { 365 Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth + 366 " mBinding = " + mBinding); 367 } 368 Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); 369 mHandler.sendMessage(msg); 370 } 371 public boolean enableNoAutoConnect() 372 { 373 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 374 "Need BLUETOOTH ADMIN permission"); 375 376 if (DBG) { 377 Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth + 378 " mBinding = " + mBinding); 379 } 380 int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); 381 382 if (callingAppId != Process.NFC_UID) { 383 throw new SecurityException("no permission to enable Bluetooth quietly"); 384 } 385 386 synchronized(mReceiver) { 387 mQuietEnableExternal = true; 388 mEnableExternal = true; 389 sendEnableMsg(true); 390 } 391 return true; 392 393 } 394 public boolean enable() { 395 if ((Binder.getCallingUid() != Process.SYSTEM_UID) && 396 (!checkIfCallerIsForegroundUser())) { 397 Log.w(TAG,"enable(): not allowed for non-active and non system user"); 398 return false; 399 } 400 401 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 402 "Need BLUETOOTH ADMIN permission"); 403 if (DBG) { 404 Log.d(TAG,"enable(): mBluetooth =" + mBluetooth + 405 " mBinding = " + mBinding); 406 } 407 408 synchronized(mReceiver) { 409 mQuietEnableExternal = false; 410 mEnableExternal = true; 411 // waive WRITE_SECURE_SETTINGS permission check 412 long callingIdentity = Binder.clearCallingIdentity(); 413 persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); 414 Binder.restoreCallingIdentity(callingIdentity); 415 sendEnableMsg(false); 416 } 417 return true; 418 } 419 420 public boolean disable(boolean persist) { 421 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 422 "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); 423 424 if ((Binder.getCallingUid() != Process.SYSTEM_UID) && 425 (!checkIfCallerIsForegroundUser())) { 426 Log.w(TAG,"disable(): not allowed for non-active and non system user"); 427 return false; 428 } 429 430 if (DBG) { 431 Log.d(TAG,"disable(): mBluetooth = " + mBluetooth + 432 " mBinding = " + mBinding); 433 } 434 435 synchronized(mReceiver) { 436 if (persist) { 437 // waive WRITE_SECURE_SETTINGS permission check 438 long callingIdentity = Binder.clearCallingIdentity(); 439 persistBluetoothSetting(BLUETOOTH_OFF); 440 Binder.restoreCallingIdentity(callingIdentity); 441 } 442 mEnableExternal = false; 443 sendDisableMsg(); 444 } 445 return true; 446 } 447 448 public void unbindAndFinish() { 449 if (DBG) { 450 Log.d(TAG,"unbindAndFinish(): " + mBluetooth + 451 " mBinding = " + mBinding); 452 } 453 454 synchronized (mConnection) { 455 if (mUnbinding) return; 456 mUnbinding = true; 457 if (mBluetooth != null) { 458 if (!mConnection.isGetNameAddressOnly()) { 459 //Unregister callback object 460 try { 461 mBluetooth.unregisterCallback(mBluetoothCallback); 462 } catch (RemoteException re) { 463 Log.e(TAG, "Unable to unregister BluetoothCallback",re); 464 } 465 } 466 if (DBG) Log.d(TAG, "Sending unbind request."); 467 mBluetooth = null; 468 //Unbind 469 mContext.unbindService(mConnection); 470 mUnbinding = false; 471 mBinding = false; 472 } else { 473 mUnbinding=false; 474 } 475 } 476 } 477 478 public IBluetoothGatt getBluetoothGatt() { 479 // sync protection 480 return mBluetoothGatt; 481 } 482 483 private void sendBluetoothStateCallback(boolean isUp) { 484 int n = mStateChangeCallbacks.beginBroadcast(); 485 if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); 486 for (int i=0; i <n;i++) { 487 try { 488 mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp); 489 } catch (RemoteException e) { 490 Log.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e); 491 } 492 } 493 mStateChangeCallbacks.finishBroadcast(); 494 } 495 496 /** 497 * Inform BluetoothAdapter instances that Adapter service is up 498 */ 499 private void sendBluetoothServiceUpCallback() { 500 if (!mConnection.isGetNameAddressOnly()) { 501 if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks"); 502 int n = mCallbacks.beginBroadcast(); 503 Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers."); 504 for (int i=0; i <n;i++) { 505 try { 506 mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth); 507 } catch (RemoteException e) { 508 Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e); 509 } 510 } 511 mCallbacks.finishBroadcast(); 512 } 513 } 514 /** 515 * Inform BluetoothAdapter instances that Adapter service is down 516 */ 517 private void sendBluetoothServiceDownCallback() { 518 if (!mConnection.isGetNameAddressOnly()) { 519 if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks"); 520 int n = mCallbacks.beginBroadcast(); 521 Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers."); 522 for (int i=0; i <n;i++) { 523 try { 524 mCallbacks.getBroadcastItem(i).onBluetoothServiceDown(); 525 } catch (RemoteException e) { 526 Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e); 527 } 528 } 529 mCallbacks.finishBroadcast(); 530 } 531 } 532 public String getAddress() { 533 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 534 "Need BLUETOOTH permission"); 535 536 if ((Binder.getCallingUid() != Process.SYSTEM_UID) && 537 (!checkIfCallerIsForegroundUser())) { 538 Log.w(TAG,"getAddress(): not allowed for non-active and non system user"); 539 return null; 540 } 541 542 synchronized(mConnection) { 543 if (mBluetooth != null) { 544 try { 545 return mBluetooth.getAddress(); 546 } catch (RemoteException e) { 547 Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e); 548 } 549 } 550 } 551 // mAddress is accessed from outside. 552 // It is alright without a lock. Here, bluetooth is off, no other thread is 553 // changing mAddress 554 return mAddress; 555 } 556 557 public String getName() { 558 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 559 "Need BLUETOOTH permission"); 560 561 if ((Binder.getCallingUid() != Process.SYSTEM_UID) && 562 (!checkIfCallerIsForegroundUser())) { 563 Log.w(TAG,"getName(): not allowed for non-active and non system user"); 564 return null; 565 } 566 567 synchronized(mConnection) { 568 if (mBluetooth != null) { 569 try { 570 return mBluetooth.getName(); 571 } catch (RemoteException e) { 572 Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e); 573 } 574 } 575 } 576 // mName is accessed from outside. 577 // It alright without a lock. Here, bluetooth is off, no other thread is 578 // changing mName 579 return mName; 580 } 581 582 private class BluetoothServiceConnection implements ServiceConnection { 583 584 private boolean mGetNameAddressOnly; 585 586 public void setGetNameAddressOnly(boolean getOnly) { 587 mGetNameAddressOnly = getOnly; 588 } 589 590 public boolean isGetNameAddressOnly() { 591 return mGetNameAddressOnly; 592 } 593 594 public void onServiceConnected(ComponentName className, IBinder service) { 595 if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName()); 596 Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); 597 // TBD if (className.getClassName().equals(IBluetooth.class.getName())) { 598 if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { 599 msg.arg1 = SERVICE_IBLUETOOTH; 600 // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) { 601 } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { 602 msg.arg1 = SERVICE_IBLUETOOTHGATT; 603 } else { 604 Log.e(TAG, "Unknown service connected: " + className.getClassName()); 605 return; 606 } 607 msg.obj = service; 608 mHandler.sendMessage(msg); 609 } 610 611 public void onServiceDisconnected(ComponentName className) { 612 // Called if we unexpected disconnected. 613 if (DBG) Log.d(TAG, "BluetoothServiceConnection, disconnected: " + 614 className.getClassName()); 615 Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); 616 if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { 617 msg.arg1 = SERVICE_IBLUETOOTH; 618 } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { 619 msg.arg1 = SERVICE_IBLUETOOTHGATT; 620 } else { 621 Log.e(TAG, "Unknown service disconnected: " + className.getClassName()); 622 return; 623 } 624 mHandler.sendMessage(msg); 625 } 626 } 627 628 private BluetoothServiceConnection mConnection = new BluetoothServiceConnection(); 629 630 private class BluetoothHandler extends Handler { 631 public BluetoothHandler(Looper looper) { 632 super(looper); 633 } 634 635 @Override 636 public void handleMessage(Message msg) { 637 if (DBG) Log.d (TAG, "Message: " + msg.what); 638 switch (msg.what) { 639 case MESSAGE_GET_NAME_AND_ADDRESS: { 640 if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS"); 641 synchronized(mConnection) { 642 //Start bind request 643 if ((mBluetooth == null) && (!mBinding)) { 644 if (DBG) Log.d(TAG, "Binding to service to get name and address"); 645 mConnection.setGetNameAddressOnly(true); 646 //Start bind timeout and bind 647 Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); 648 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); 649 Intent i = new Intent(IBluetooth.class.getName()); 650 if (!mContext.bindServiceAsUser(i, mConnection, 651 Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) { 652 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); 653 Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName()); 654 } else { 655 mBinding = true; 656 } 657 } 658 else { 659 Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS); 660 saveMsg.arg1 = 0; 661 if (mBluetooth != null) { 662 mHandler.sendMessage(saveMsg); 663 } else { 664 // if enable is also called to bind the service 665 // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED 666 mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS); 667 } 668 } 669 } 670 break; 671 } 672 case MESSAGE_SAVE_NAME_AND_ADDRESS: { 673 boolean unbind = false; 674 if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS"); 675 synchronized(mConnection) { 676 if (!mEnable && mBluetooth != null) { 677 try { 678 mBluetooth.enable(); 679 } catch (RemoteException e) { 680 Log.e(TAG,"Unable to call enable()",e); 681 } 682 } 683 } 684 if (mBluetooth != null) waitForOnOff(true, false); 685 synchronized(mConnection) { 686 if (mBluetooth != null) { 687 String name = null; 688 String address = null; 689 try { 690 name = mBluetooth.getName(); 691 address = mBluetooth.getAddress(); 692 } catch (RemoteException re) { 693 Log.e(TAG,"",re); 694 } 695 696 if (name != null && address != null) { 697 storeNameAndAddress(name,address); 698 if (mConnection.isGetNameAddressOnly()) { 699 unbind = true; 700 } 701 } else { 702 if (msg.arg1 < MAX_SAVE_RETRIES) { 703 Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS); 704 retryMsg.arg1= 1+msg.arg1; 705 if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1); 706 mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS); 707 } else { 708 Log.w(TAG,"Maximum name/address remote retrieval retry exceeded"); 709 if (mConnection.isGetNameAddressOnly()) { 710 unbind = true; 711 } 712 } 713 } 714 if (!mEnable) { 715 try { 716 mBluetooth.disable(); 717 } catch (RemoteException e) { 718 Log.e(TAG,"Unable to call disable()",e); 719 } 720 } 721 } else { 722 // rebind service by Request GET NAME AND ADDRESS 723 // if service is unbinded by disable or 724 // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received 725 Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); 726 mHandler.sendMessage(getMsg); 727 } 728 } 729 if (!mEnable && mBluetooth != null) waitForOnOff(false, true); 730 if (unbind) { 731 unbindAndFinish(); 732 } 733 break; 734 } 735 case MESSAGE_ENABLE: 736 if (DBG) { 737 Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth); 738 } 739 mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); 740 mEnable = true; 741 handleEnable(msg.arg1 == 1); 742 break; 743 744 case MESSAGE_DISABLE: 745 mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); 746 if (mEnable && mBluetooth != null) { 747 waitForOnOff(true, false); 748 mEnable = false; 749 handleDisable(); 750 waitForOnOff(false, false); 751 } else { 752 mEnable = false; 753 handleDisable(); 754 } 755 break; 756 757 case MESSAGE_REGISTER_ADAPTER: 758 { 759 IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; 760 boolean added = mCallbacks.register(callback); 761 Log.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added ); 762 } 763 break; 764 case MESSAGE_UNREGISTER_ADAPTER: 765 { 766 IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; 767 boolean removed = mCallbacks.unregister(callback); 768 Log.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed); 769 break; 770 } 771 case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: 772 { 773 IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; 774 mStateChangeCallbacks.register(callback); 775 break; 776 } 777 case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: 778 { 779 IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; 780 mStateChangeCallbacks.unregister(callback); 781 break; 782 } 783 case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: 784 { 785 if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1); 786 787 IBinder service = (IBinder) msg.obj; 788 synchronized(mConnection) { 789 if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { 790 mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); 791 break; 792 } // else must be SERVICE_IBLUETOOTH 793 794 //Remove timeout 795 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); 796 797 mBinding = false; 798 mBluetooth = IBluetooth.Stub.asInterface(service); 799 800 if (mConnection.isGetNameAddressOnly()) { 801 //Request GET NAME AND ADDRESS 802 Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); 803 mHandler.sendMessage(getMsg); 804 if (!mEnable) return; 805 } 806 807 mConnection.setGetNameAddressOnly(false); 808 //Register callback object 809 try { 810 mBluetooth.registerCallback(mBluetoothCallback); 811 } catch (RemoteException re) { 812 Log.e(TAG, "Unable to register BluetoothCallback",re); 813 } 814 //Inform BluetoothAdapter instances that service is up 815 sendBluetoothServiceUpCallback(); 816 817 //Do enable request 818 try { 819 if (mQuietEnable == false) { 820 if(!mBluetooth.enable()) { 821 Log.e(TAG,"IBluetooth.enable() returned false"); 822 } 823 } 824 else 825 { 826 if(!mBluetooth.enableNoAutoConnect()) { 827 Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false"); 828 } 829 } 830 } catch (RemoteException e) { 831 Log.e(TAG,"Unable to call enable()",e); 832 } 833 } 834 835 if (!mEnable) { 836 waitForOnOff(true, false); 837 handleDisable(); 838 waitForOnOff(false, false); 839 } 840 break; 841 } 842 case MESSAGE_TIMEOUT_BIND: { 843 Log.e(TAG, "MESSAGE_TIMEOUT_BIND"); 844 synchronized(mConnection) { 845 mBinding = false; 846 } 847 break; 848 } 849 case MESSAGE_BLUETOOTH_STATE_CHANGE: 850 { 851 int prevState = msg.arg1; 852 int newState = msg.arg2; 853 if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState); 854 mState = newState; 855 bluetoothStateChangeHandler(prevState, newState); 856 // handle error state transition case from TURNING_ON to OFF 857 // unbind and rebind bluetooth service and enable bluetooth 858 if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && 859 (newState == BluetoothAdapter.STATE_OFF) && 860 (mBluetooth != null) && mEnable) { 861 recoverBluetoothServiceFromError(); 862 } 863 if (newState == BluetoothAdapter.STATE_ON) { 864 // bluetooth is working, reset the counter 865 if (mErrorRecoveryRetryCounter != 0) { 866 Log.w(TAG, "bluetooth is recovered from error"); 867 mErrorRecoveryRetryCounter = 0; 868 } 869 } 870 break; 871 } 872 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: 873 { 874 Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1); 875 synchronized(mConnection) { 876 if (msg.arg1 == SERVICE_IBLUETOOTH) { 877 // if service is unbinded already, do nothing and return 878 if (mBluetooth == null) break; 879 mBluetooth = null; 880 } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { 881 mBluetoothGatt = null; 882 break; 883 } else { 884 Log.e(TAG, "Bad msg.arg1: " + msg.arg1); 885 break; 886 } 887 } 888 889 if (mEnable) { 890 mEnable = false; 891 // Send a Bluetooth Restart message 892 Message restartMsg = mHandler.obtainMessage( 893 MESSAGE_RESTART_BLUETOOTH_SERVICE); 894 mHandler.sendMessageDelayed(restartMsg, 895 SERVICE_RESTART_TIME_MS); 896 } 897 898 if (!mConnection.isGetNameAddressOnly()) { 899 sendBluetoothServiceDownCallback(); 900 901 // Send BT state broadcast to update 902 // the BT icon correctly 903 if ((mState == BluetoothAdapter.STATE_TURNING_ON) || 904 (mState == BluetoothAdapter.STATE_ON)) { 905 bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, 906 BluetoothAdapter.STATE_TURNING_OFF); 907 mState = BluetoothAdapter.STATE_TURNING_OFF; 908 } 909 if (mState == BluetoothAdapter.STATE_TURNING_OFF) { 910 bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, 911 BluetoothAdapter.STATE_OFF); 912 } 913 914 mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); 915 mState = BluetoothAdapter.STATE_OFF; 916 } 917 break; 918 } 919 case MESSAGE_RESTART_BLUETOOTH_SERVICE: 920 { 921 Log.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:" 922 +" Restart IBluetooth service"); 923 /* Enable without persisting the setting as 924 it doesnt change when IBluetooth 925 service restarts */ 926 mEnable = true; 927 handleEnable(mQuietEnable); 928 break; 929 } 930 931 case MESSAGE_TIMEOUT_UNBIND: 932 { 933 Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); 934 synchronized(mConnection) { 935 mUnbinding = false; 936 } 937 break; 938 } 939 940 case MESSAGE_USER_SWITCHED: 941 { 942 if (DBG) { 943 Log.d(TAG, "MESSAGE_USER_SWITCHED"); 944 } 945 mHandler.removeMessages(MESSAGE_USER_SWITCHED); 946 /* disable and enable BT when detect a user switch */ 947 if (mEnable && mBluetooth != null) { 948 synchronized (mConnection) { 949 if (mBluetooth != null) { 950 //Unregister callback object 951 try { 952 mBluetooth.unregisterCallback(mBluetoothCallback); 953 } catch (RemoteException re) { 954 Log.e(TAG, "Unable to unregister",re); 955 } 956 } 957 } 958 959 if (mState == BluetoothAdapter.STATE_TURNING_OFF) { 960 // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE 961 bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); 962 mState = BluetoothAdapter.STATE_OFF; 963 } 964 if (mState == BluetoothAdapter.STATE_OFF) { 965 bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); 966 mState = BluetoothAdapter.STATE_TURNING_ON; 967 } 968 969 waitForOnOff(true, false); 970 971 if (mState == BluetoothAdapter.STATE_TURNING_ON) { 972 bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); 973 } 974 975 // disable 976 handleDisable(); 977 // Pbap service need receive STATE_TURNING_OFF intent to close 978 bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, 979 BluetoothAdapter.STATE_TURNING_OFF); 980 981 waitForOnOff(false, true); 982 983 bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, 984 BluetoothAdapter.STATE_OFF); 985 sendBluetoothServiceDownCallback(); 986 synchronized (mConnection) { 987 if (mBluetooth != null) { 988 mBluetooth = null; 989 //Unbind 990 mContext.unbindService(mConnection); 991 } 992 } 993 SystemClock.sleep(100); 994 995 mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); 996 mState = BluetoothAdapter.STATE_OFF; 997 // enable 998 handleEnable(mQuietEnable); 999 } else if (mBinding || mBluetooth != null) { 1000 Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); 1001 userMsg.arg2 = 1 + msg.arg2; 1002 // if user is switched when service is being binding 1003 // delay sending MESSAGE_USER_SWITCHED 1004 mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS); 1005 if (DBG) { 1006 Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2); 1007 } 1008 } 1009 break; 1010 } 1011 } 1012 } 1013 } 1014 1015 private void handleEnable(boolean quietMode) { 1016 mQuietEnable = quietMode; 1017 1018 synchronized(mConnection) { 1019 if ((mBluetooth == null) && (!mBinding)) { 1020 //Start bind timeout and bind 1021 Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); 1022 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); 1023 mConnection.setGetNameAddressOnly(false); 1024 Intent i = new Intent(IBluetooth.class.getName()); 1025 if (!mContext.bindServiceAsUser(i, mConnection,Context.BIND_AUTO_CREATE, 1026 UserHandle.CURRENT)) { 1027 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); 1028 Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName()); 1029 } else { 1030 mBinding = true; 1031 } 1032 } else if (mBluetooth != null) { 1033 if (mConnection.isGetNameAddressOnly()) { 1034 // if GetNameAddressOnly is set, we can clear this flag, 1035 // so the service won't be unbind 1036 // after name and address are saved 1037 mConnection.setGetNameAddressOnly(false); 1038 //Register callback object 1039 try { 1040 mBluetooth.registerCallback(mBluetoothCallback); 1041 } catch (RemoteException re) { 1042 Log.e(TAG, "Unable to register BluetoothCallback",re); 1043 } 1044 //Inform BluetoothAdapter instances that service is up 1045 sendBluetoothServiceUpCallback(); 1046 } 1047 1048 //Enable bluetooth 1049 try { 1050 if (!mQuietEnable) { 1051 if(!mBluetooth.enable()) { 1052 Log.e(TAG,"IBluetooth.enable() returned false"); 1053 } 1054 } 1055 else { 1056 if(!mBluetooth.enableNoAutoConnect()) { 1057 Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false"); 1058 } 1059 } 1060 } catch (RemoteException e) { 1061 Log.e(TAG,"Unable to call enable()",e); 1062 } 1063 } 1064 } 1065 } 1066 1067 private void handleDisable() { 1068 synchronized(mConnection) { 1069 // don't need to disable if GetNameAddressOnly is set, 1070 // service will be unbinded after Name and Address are saved 1071 if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) { 1072 if (DBG) Log.d(TAG,"Sending off request."); 1073 1074 try { 1075 if(!mBluetooth.disable()) { 1076 Log.e(TAG,"IBluetooth.disable() returned false"); 1077 } 1078 } catch (RemoteException e) { 1079 Log.e(TAG,"Unable to call disable()",e); 1080 } 1081 } 1082 } 1083 } 1084 1085 private boolean checkIfCallerIsForegroundUser() { 1086 int foregroundUser; 1087 int callingUser = UserHandle.getCallingUserId(); 1088 int callingUid = Binder.getCallingUid(); 1089 long callingIdentity = Binder.clearCallingIdentity(); 1090 int callingAppId = UserHandle.getAppId(callingUid); 1091 boolean valid = false; 1092 try { 1093 foregroundUser = ActivityManager.getCurrentUser(); 1094 valid = (callingUser == foregroundUser) || 1095 callingAppId == Process.NFC_UID; 1096 if (DBG) { 1097 Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid 1098 + " callingUser=" + callingUser 1099 + " foregroundUser=" + foregroundUser); 1100 } 1101 } finally { 1102 Binder.restoreCallingIdentity(callingIdentity); 1103 } 1104 return valid; 1105 } 1106 1107 private void bluetoothStateChangeHandler(int prevState, int newState) { 1108 if (prevState != newState) { 1109 //Notify all proxy objects first of adapter state change 1110 if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) { 1111 boolean isUp = (newState==BluetoothAdapter.STATE_ON); 1112 sendBluetoothStateCallback(isUp); 1113 1114 if (isUp) { 1115 // connect to GattService 1116 if (mContext.getPackageManager().hasSystemFeature( 1117 PackageManager.FEATURE_BLUETOOTH_LE)) { 1118 Intent i = new Intent(IBluetoothGatt.class.getName()); 1119 if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE, 1120 UserHandle.CURRENT)) { 1121 Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName()); 1122 } 1123 } 1124 } else { 1125 //If Bluetooth is off, send service down event to proxy objects, and unbind 1126 if (!isUp && canUnbindBluetoothService()) { 1127 sendBluetoothServiceDownCallback(); 1128 unbindAndFinish(); 1129 } 1130 } 1131 } 1132 1133 //Send broadcast message to everyone else 1134 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); 1135 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); 1136 intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); 1137 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1138 if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState); 1139 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 1140 BLUETOOTH_PERM); 1141 } 1142 } 1143 1144 /** 1145 * if on is true, wait for state become ON 1146 * if off is true, wait for state become OFF 1147 * if both on and off are false, wait for state not ON 1148 */ 1149 private boolean waitForOnOff(boolean on, boolean off) { 1150 int i = 0; 1151 while (i < 10) { 1152 synchronized(mConnection) { 1153 try { 1154 if (mBluetooth == null) break; 1155 if (on) { 1156 if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; 1157 } else if (off) { 1158 if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; 1159 } else { 1160 if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; 1161 } 1162 } catch (RemoteException e) { 1163 Log.e(TAG, "getState()", e); 1164 break; 1165 } 1166 } 1167 if (on || off) { 1168 SystemClock.sleep(300); 1169 } else { 1170 SystemClock.sleep(50); 1171 } 1172 i++; 1173 } 1174 Log.e(TAG,"waitForOnOff time out"); 1175 return false; 1176 } 1177 1178 private void sendDisableMsg() { 1179 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); 1180 } 1181 1182 private void sendEnableMsg(boolean quietMode) { 1183 mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, 1184 quietMode ? 1 : 0, 0)); 1185 } 1186 1187 private boolean canUnbindBluetoothService() { 1188 synchronized(mConnection) { 1189 //Only unbind with mEnable flag not set 1190 //For race condition: disable and enable back-to-back 1191 //Avoid unbind right after enable due to callback from disable 1192 //Only unbind with Bluetooth at OFF state 1193 //Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message 1194 try { 1195 if (mEnable || (mBluetooth == null)) return false; 1196 if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; 1197 return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); 1198 } catch (RemoteException e) { 1199 Log.e(TAG, "getState()", e); 1200 } 1201 } 1202 return false; 1203 } 1204 1205 private void recoverBluetoothServiceFromError() { 1206 Log.e(TAG,"recoverBluetoothServiceFromError"); 1207 synchronized (mConnection) { 1208 if (mBluetooth != null) { 1209 //Unregister callback object 1210 try { 1211 mBluetooth.unregisterCallback(mBluetoothCallback); 1212 } catch (RemoteException re) { 1213 Log.e(TAG, "Unable to unregister",re); 1214 } 1215 } 1216 } 1217 1218 SystemClock.sleep(500); 1219 1220 // disable 1221 handleDisable(); 1222 1223 waitForOnOff(false, true); 1224 1225 sendBluetoothServiceDownCallback(); 1226 synchronized (mConnection) { 1227 if (mBluetooth != null) { 1228 mBluetooth = null; 1229 //Unbind 1230 mContext.unbindService(mConnection); 1231 } 1232 } 1233 1234 mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); 1235 mState = BluetoothAdapter.STATE_OFF; 1236 1237 mEnable = false; 1238 1239 if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) { 1240 // Send a Bluetooth Restart message to reenable bluetooth 1241 Message restartMsg = mHandler.obtainMessage( 1242 MESSAGE_RESTART_BLUETOOTH_SERVICE); 1243 mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS); 1244 } else { 1245 // todo: notify user to power down and power up phone to make bluetooth work. 1246 } 1247 } 1248 } 1249