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