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