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