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