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