BluetoothManagerService.java revision fa0fd39a4bf296d254aa398c1b19ec960efa641d
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.IBluetoothCallback;
23import android.bluetooth.IBluetoothManager;
24import android.bluetooth.IBluetoothManagerCallback;
25import android.bluetooth.IBluetoothStateChangeCallback;
26import android.content.BroadcastReceiver;
27import android.content.ComponentName;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.ServiceConnection;
33import android.os.Binder;
34import android.os.Handler;
35import android.os.HandlerThread;
36import android.os.IBinder;
37import android.os.Looper;
38import android.os.Message;
39import android.os.Process;
40import android.os.RemoteCallbackList;
41import android.os.RemoteException;
42import android.os.SystemClock;
43import android.os.UserHandle;
44import android.provider.Settings;
45import android.util.Log;
46import java.util.ArrayList;
47import java.util.List;
48class BluetoothManagerService extends IBluetoothManager.Stub {
49    private static final String TAG = "BluetoothManagerService";
50    private static final boolean DBG = true;
51
52    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
53    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
54    private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED";
55    private static final String EXTRA_ACTION="action";
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 delay MESSAGE_USER_SWITCHED
63    private static final int USER_SWITCHED_TIME_MS = 200;
64
65    private static final int MESSAGE_ENABLE = 1;
66    private static final int MESSAGE_DISABLE = 2;
67    private static final int MESSAGE_REGISTER_ADAPTER = 20;
68    private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
69    private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
70    private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
71    private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
72    private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
73    private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42;
74    private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60;
75    private static final int MESSAGE_TIMEOUT_BIND =100;
76    private static final int MESSAGE_TIMEOUT_UNBIND =101;
77    private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
78    private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
79    private static final int MESSAGE_USER_SWITCHED = 300;
80    private static final int MAX_SAVE_RETRIES=3;
81
82    private final Context mContext;
83
84    // Locks are not provided for mName and mAddress.
85    // They are accessed in handler or broadcast receiver, same thread context.
86    private String mAddress;
87    private String mName;
88    private final ContentResolver mContentResolver;
89    private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
90    private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
91    private IBluetooth mBluetooth;
92    private boolean mBinding;
93    private boolean mUnbinding;
94    private boolean mQuietEnable = false;
95    private boolean mEnable;
96    private int mState;
97    private HandlerThread mThread;
98    private final BluetoothHandler mHandler;
99
100    private void registerForAirplaneMode(IntentFilter filter) {
101        final ContentResolver resolver = mContext.getContentResolver();
102        final String airplaneModeRadios = Settings.Global.getString(resolver,
103                Settings.Global.AIRPLANE_MODE_RADIOS);
104        final String toggleableRadios = Settings.Global.getString(resolver,
105                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
106        boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true :
107                airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH);
108        if (mIsAirplaneSensitive) {
109            filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
110        }
111    }
112
113    private final IBluetoothCallback mBluetoothCallback =  new IBluetoothCallback.Stub() {
114        @Override
115        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException  {
116            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState);
117            mHandler.sendMessage(msg);
118        }
119    };
120
121    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
122        @Override
123        public void onReceive(Context context, Intent intent) {
124            String action = intent.getAction();
125            if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
126                String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
127                if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName);
128                if (newName != null) {
129                    storeNameAndAddress(newName, null);
130                }
131            } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
132                if (isAirplaneModeOn()) {
133                    // disable without persisting the setting
134                    mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE,
135                           0, 0));
136                } else if (isBluetoothPersistedStateOn()) {
137                    // enable without persisting the setting
138                    mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
139                           0, 0));
140                }
141            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
142                mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED,
143                       intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
144            }
145        }
146    };
147
148    BluetoothManagerService(Context context) {
149        mThread = new HandlerThread("BluetoothManager");
150        mThread.start();
151        mHandler = new BluetoothHandler(mThread.getLooper());
152
153        mContext = context;
154        mBluetooth = null;
155        mBinding = false;
156        mUnbinding = false;
157        mEnable = false;
158        mState = BluetoothAdapter.STATE_OFF;
159        mAddress = null;
160        mName = null;
161        mContentResolver = context.getContentResolver();
162        mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
163        mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
164        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
165        filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
166        filter.addAction(Intent.ACTION_USER_SWITCHED);
167        registerForAirplaneMode(filter);
168        mContext.registerReceiver(mReceiver, filter);
169        boolean airplaneModeOn = isAirplaneModeOn();
170        boolean bluetoothOn = isBluetoothPersistedStateOn();
171        loadStoredNameAndAddress();
172        if (DBG) Log.d(TAG, "airplaneModeOn: " + airplaneModeOn + " bluetoothOn: " + bluetoothOn);
173        if (bluetoothOn) {
174            //Enable
175            if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
176            enableHelper();
177        } else if (!isNameAndAddressSet()) {
178            //Sync the Bluetooth name and address from the Bluetooth Adapter
179            if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
180            getNameAndAddress();
181        }
182    }
183
184    /**
185     *  Returns true if airplane mode is currently on
186     */
187    private final boolean isAirplaneModeOn() {
188        return Settings.Global.getInt(mContext.getContentResolver(),
189                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
190    }
191
192    /**
193     *  Returns true if the Bluetooth saved state is "on"
194     */
195    private final boolean isBluetoothPersistedStateOn() {
196        return Settings.Global.getInt(mContentResolver,
197                Settings.Global.BLUETOOTH_ON, 0) ==1;
198    }
199
200    /**
201     *  Save the Bluetooth on/off state
202     *
203     */
204    private void persistBluetoothSetting(boolean setOn) {
205        Settings.Global.putInt(mContext.getContentResolver(),
206                               Settings.Global.BLUETOOTH_ON,
207                               setOn ? 1 : 0);
208    }
209
210    /**
211     * Returns true if the Bluetooth Adapter's name and address is
212     * locally cached
213     * @return
214     */
215    private boolean isNameAndAddressSet() {
216        return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0;
217    }
218
219    /**
220     * Retrieve the Bluetooth Adapter's name and address and save it in
221     * in the local cache
222     */
223    private void loadStoredNameAndAddress() {
224        if (DBG) Log.d(TAG, "Loading stored name and address");
225        mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
226        mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
227        if (mName == null || mAddress == null) {
228            if (DBG) Log.d(TAG, "Name or address not cached...");
229        }
230    }
231
232    /**
233     * Save the Bluetooth name and address in the persistent store.
234     * Only non-null values will be saved.
235     * @param name
236     * @param address
237     */
238    private void storeNameAndAddress(String name, String address) {
239        if (name != null) {
240            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
241            mName = name;
242            if (DBG) Log.d(TAG,"Stored Bluetooth name: " +
243                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
244        }
245
246        if (address != null) {
247            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
248            mAddress=address;
249            if (DBG)  Log.d(TAG,"Stored Bluetoothaddress: " +
250                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
251        }
252    }
253
254    public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
255        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
256        msg.obj = callback;
257        mHandler.sendMessage(msg);
258        synchronized(mConnection) {
259            return mBluetooth;
260        }
261    }
262
263    public void unregisterAdapter(IBluetoothManagerCallback callback) {
264        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
265                                                "Need BLUETOOTH permission");
266        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
267        msg.obj = callback;
268        mHandler.sendMessage(msg);
269    }
270
271    public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
272        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
273                                                "Need BLUETOOTH permission");
274        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
275        msg.obj = callback;
276        mHandler.sendMessage(msg);
277    }
278
279    public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
280        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
281                                                "Need BLUETOOTH permission");
282        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
283        msg.obj = callback;
284        mHandler.sendMessage(msg);
285    }
286
287    public boolean isEnabled() {
288        if (!checkIfCallerIsForegroundUser()) {
289            Log.w(TAG,"isEnabled(): not allowed for non-active user");
290            return false;
291        }
292
293        synchronized(mConnection) {
294            try {
295                return (mBluetooth != null && mBluetooth.isEnabled());
296            } catch (RemoteException e) {
297                Log.e(TAG, "isEnabled()", e);
298            }
299        }
300        return false;
301    }
302
303    public void getNameAndAddress() {
304        if (DBG) {
305            Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
306                  " mBinding = " + mBinding);
307        }
308        Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
309        mHandler.sendMessage(msg);
310    }
311    public boolean enableNoAutoConnect()
312    {
313        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
314                                                "Need BLUETOOTH ADMIN permission");
315
316        if (!checkIfCallerIsForegroundUser()) {
317            Log.w(TAG,"enableNoAutoConnect(): not allowed for non-active user");
318            return false;
319        }
320
321        if (DBG) {
322            Log.d(TAG,"enableNoAutoConnect():  mBluetooth =" + mBluetooth +
323                    " mBinding = " + mBinding);
324        }
325        if (Binder.getCallingUid() != Process.NFC_UID) {
326            throw new SecurityException("no permission to enable Bluetooth quietly");
327        }
328        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
329        msg.arg1=0; //No persist
330        msg.arg2=1; //Quiet mode
331        mHandler.sendMessage(msg);
332        return true;
333
334    }
335    public boolean enable() {
336        if (!checkIfCallerIsForegroundUser()) {
337            Log.w(TAG,"enable(): not allowed for non-active user");
338            return false;
339        }
340
341        return enableHelper();
342    }
343
344    public boolean disable(boolean persist) {
345        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
346                                                "Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
347
348        if (!checkIfCallerIsForegroundUser()) {
349            Log.w(TAG,"disable(): not allowed for non-active user");
350            return false;
351        }
352
353        if (DBG) {
354            Log.d(TAG,"disable(): mBluetooth = " + mBluetooth +
355                " mBinding = " + mBinding);
356        }
357
358        Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
359        msg.arg1=(persist?1:0);
360        mHandler.sendMessage(msg);
361        return true;
362    }
363
364    public void unbindAndFinish() {
365        if (DBG) {
366            Log.d(TAG,"unbindAndFinish(): " + mBluetooth +
367                " mBinding = " + mBinding);
368        }
369
370        synchronized (mConnection) {
371            if (mUnbinding) return;
372            mUnbinding = true;
373            if (mBluetooth != null) {
374                if (!mConnection.isGetNameAddressOnly()) {
375                    //Unregister callback object
376                    try {
377                        mBluetooth.unregisterCallback(mBluetoothCallback);
378                    } catch (RemoteException re) {
379                        Log.e(TAG, "Unable to unregister BluetoothCallback",re);
380                    }
381                }
382                if (DBG) Log.d(TAG, "Sending unbind request.");
383                mBluetooth = null;
384                //Unbind
385                mContext.unbindService(mConnection);
386                mUnbinding = false;
387                mBinding = false;
388            } else {
389                mUnbinding=false;
390            }
391        }
392    }
393
394    private void sendBluetoothStateCallback(boolean isUp) {
395        int n = mStateChangeCallbacks.beginBroadcast();
396        if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
397        for (int i=0; i <n;i++) {
398            try {
399                mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
400            } catch (RemoteException e) {
401                Log.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e);
402            }
403        }
404        mStateChangeCallbacks.finishBroadcast();
405    }
406
407    /**
408     * Inform BluetoothAdapter instances that Adapter service is up
409     */
410    private void sendBluetoothServiceUpCallback() {
411        if (!mConnection.isGetNameAddressOnly()) {
412            if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
413            int n = mCallbacks.beginBroadcast();
414            Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
415            for (int i=0; i <n;i++) {
416                try {
417                    mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
418                }  catch (RemoteException e) {
419                    Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
420                }
421            }
422            mCallbacks.finishBroadcast();
423        }
424    }
425    /**
426     * Inform BluetoothAdapter instances that Adapter service is down
427     */
428    private void sendBluetoothServiceDownCallback() {
429        if (!mConnection.isGetNameAddressOnly()) {
430            if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
431            int n = mCallbacks.beginBroadcast();
432            Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
433            for (int i=0; i <n;i++) {
434                try {
435                    mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
436                }  catch (RemoteException e) {
437                    Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
438                }
439            }
440            mCallbacks.finishBroadcast();
441        }
442    }
443    public String getAddress() {
444        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
445                                                "Need BLUETOOTH ADMIN permission");
446
447        if (!checkIfCallerIsForegroundUser()) {
448            Log.w(TAG,"getAddress(): not allowed for non-active user");
449            return mAddress;
450        }
451
452        synchronized(mConnection) {
453            if (mBluetooth != null) {
454                try {
455                    return mBluetooth.getAddress();
456                } catch (RemoteException e) {
457                    Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
458                }
459            }
460        }
461        // mAddress is accessed from outside.
462        // It is alright without a lock. Here, bluetooth is off, no other thread is
463        // changing mAddress
464        return mAddress;
465    }
466
467    public String getName() {
468        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
469                                                "Need BLUETOOTH ADMIN permission");
470
471        if (!checkIfCallerIsForegroundUser()) {
472            Log.w(TAG,"getName(): not allowed for non-active user");
473            return mName;
474        }
475
476        synchronized(mConnection) {
477            if (mBluetooth != null) {
478                try {
479                    return mBluetooth.getName();
480                } catch (RemoteException e) {
481                    Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
482                }
483            }
484        }
485        // mName is accessed from outside.
486        // It alright without a lock. Here, bluetooth is off, no other thread is
487        // changing mName
488        return mName;
489    }
490
491    private class BluetoothServiceConnection implements ServiceConnection {
492
493        private boolean mGetNameAddressOnly;
494
495        public void setGetNameAddressOnly(boolean getOnly) {
496            mGetNameAddressOnly = getOnly;
497        }
498
499        public boolean isGetNameAddressOnly() {
500            return mGetNameAddressOnly;
501        }
502
503        public void onServiceConnected(ComponentName className, IBinder service) {
504            if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService");
505            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
506            msg.obj = service;
507            mHandler.sendMessage(msg);
508        }
509
510        public void onServiceDisconnected(ComponentName className) {
511            // Called if we unexpected disconnected.
512            if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService");
513            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
514            mHandler.sendMessage(msg);
515        }
516    }
517
518    private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
519
520    private class BluetoothHandler extends Handler {
521        public BluetoothHandler(Looper looper) {
522            super(looper);
523        }
524
525        @Override
526        public void handleMessage(Message msg) {
527            if (DBG) Log.d (TAG, "Message: " + msg.what);
528            switch (msg.what) {
529                case MESSAGE_GET_NAME_AND_ADDRESS: {
530                    if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
531                    synchronized(mConnection) {
532                        //Start bind request
533                        if ((mBluetooth == null) && (!mBinding)) {
534                            if (DBG) Log.d(TAG, "Binding to service to get name and address");
535                            mConnection.setGetNameAddressOnly(true);
536                            //Start bind timeout and bind
537                            Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
538                            mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
539                            Intent i = new Intent(IBluetooth.class.getName());
540                            if (!mContext.bindService(i, mConnection,
541                                  Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
542                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
543                                Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
544                            } else {
545                                mBinding = true;
546                            }
547                        }
548                        else {
549                            Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
550                            saveMsg.arg1 = 0;
551                            if (mBluetooth != null) {
552                                mHandler.sendMessage(saveMsg);
553                            } else {
554                                // if enable is also called to bind the service
555                                // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
556                                mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
557                            }
558                        }
559                    }
560                    break;
561                }
562                case MESSAGE_SAVE_NAME_AND_ADDRESS: {
563                    if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS");
564                    synchronized(mConnection) {
565                        if (mBluetooth != null) {
566                            String name =  null;
567                            String address = null;
568                            try {
569                                name =  mBluetooth.getName();
570                                address = mBluetooth.getAddress();
571                            } catch (RemoteException re) {
572                                Log.e(TAG,"",re);
573                            }
574
575                            if (name != null && address != null) {
576                                storeNameAndAddress(name,address);
577                                if (mConnection.isGetNameAddressOnly()) {
578                                    unbindAndFinish();
579                                }
580                            } else {
581                                if (msg.arg1 < MAX_SAVE_RETRIES) {
582                                    Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
583                                    retryMsg.arg1= 1+msg.arg1;
584                                    if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
585                                    mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
586                                } else {
587                                    Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
588                                    if (mConnection.isGetNameAddressOnly()) {
589                                        unbindAndFinish();
590                                    }
591                                }
592                            }
593                        } else {
594                            // rebind service by Request GET NAME AND ADDRESS
595                            // if service is unbinded by disable or
596                            // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
597                            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
598                            mHandler.sendMessage(getMsg);
599                        }
600                    }
601                    break;
602                }
603                case MESSAGE_ENABLE:
604                    if (DBG) {
605                        Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
606                    }
607                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
608                    mEnable = true;
609                    handleEnable(msg.arg1 == 1, msg.arg2 ==1);
610                    break;
611
612                case MESSAGE_DISABLE:
613                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
614                    if (mEnable && mBluetooth != null) {
615                        waitForOnOff(true, false);
616                        mEnable = false;
617                        handleDisable(msg.arg1 == 1);
618                        waitForOnOff(false, false);
619                    } else {
620                        mEnable = false;
621                        handleDisable(msg.arg1 == 1);
622                    }
623                    break;
624
625                case MESSAGE_REGISTER_ADAPTER:
626                {
627                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
628                    boolean added = mCallbacks.register(callback);
629                    Log.d(TAG,"Added callback: " +  (callback == null? "null": callback)  +":" +added );
630                }
631                    break;
632                case MESSAGE_UNREGISTER_ADAPTER:
633                {
634                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
635                    boolean removed = mCallbacks.unregister(callback);
636                    Log.d(TAG,"Removed callback: " +  (callback == null? "null": callback)  +":" + removed);
637                    break;
638                }
639                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
640                {
641                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
642                    mStateChangeCallbacks.register(callback);
643                    break;
644                }
645                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
646                {
647                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
648                    mStateChangeCallbacks.unregister(callback);
649                    break;
650                }
651                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
652                {
653                    if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED");
654
655                    //Remove timeout
656                    mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
657
658                    IBinder service = (IBinder) msg.obj;
659                    synchronized(mConnection) {
660                        mBinding = false;
661                        mBluetooth = IBluetooth.Stub.asInterface(service);
662
663                        if (mConnection.isGetNameAddressOnly()) {
664                            //Request GET NAME AND ADDRESS
665                            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
666                            mHandler.sendMessage(getMsg);
667                            if (!mEnable) return;
668                        }
669
670                        mConnection.setGetNameAddressOnly(false);
671                        //Register callback object
672                        try {
673                            mBluetooth.registerCallback(mBluetoothCallback);
674                        } catch (RemoteException re) {
675                            Log.e(TAG, "Unable to register BluetoothCallback",re);
676                        }
677                        //Inform BluetoothAdapter instances that service is up
678                        sendBluetoothServiceUpCallback();
679
680                        //Check if name and address is loaded if not get it first.
681                        if (!isNameAndAddressSet()) {
682                            try {
683                                storeNameAndAddress(mBluetooth.getName(),
684                                                    mBluetooth.getAddress());
685                            } catch (RemoteException e) {Log.e(TAG, "", e);};
686                        }
687
688                        //Do enable request
689                        try {
690                            if (mQuietEnable == false) {
691                                if(!mBluetooth.enable()) {
692                                    Log.e(TAG,"IBluetooth.enable() returned false");
693                                }
694                            }
695                            else
696                            {
697                                if(!mBluetooth.enableNoAutoConnect()) {
698                                    Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
699                                }
700                            }
701                        } catch (RemoteException e) {
702                            Log.e(TAG,"Unable to call enable()",e);
703                        }
704                    }
705
706                    if (!mEnable) {
707                        waitForOnOff(true, false);
708                        handleDisable(false);
709                        waitForOnOff(false, false);
710                    }
711                    break;
712                }
713                case MESSAGE_TIMEOUT_BIND: {
714                    Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
715                    synchronized(mConnection) {
716                        mBinding = false;
717                    }
718                    break;
719                }
720                case MESSAGE_BLUETOOTH_STATE_CHANGE:
721                {
722                    int prevState = msg.arg1;
723                    int newState = msg.arg2;
724                    if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
725                    mState = newState;
726                    bluetoothStateChangeHandler(prevState, newState);
727                    break;
728                }
729                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
730                {
731                    Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
732                    synchronized(mConnection) {
733                        // if service is unbinded already, do nothing and return
734                        if (mBluetooth == null) return;
735                        mBluetooth = null;
736                    }
737
738                    if (mEnable) {
739                        mEnable = false;
740                        // Send a Bluetooth Restart message
741                        Message restartMsg = mHandler.obtainMessage(
742                            MESSAGE_RESTART_BLUETOOTH_SERVICE);
743                        mHandler.sendMessageDelayed(restartMsg,
744                            SERVICE_RESTART_TIME_MS);
745                    }
746
747                    if (!mConnection.isGetNameAddressOnly()) {
748                        sendBluetoothServiceDownCallback();
749
750                        // Send BT state broadcast to update
751                        // the BT icon correctly
752                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
753                                                    BluetoothAdapter.STATE_TURNING_OFF);
754                        mState = BluetoothAdapter.STATE_OFF;
755                    }
756                    break;
757                }
758                case MESSAGE_RESTART_BLUETOOTH_SERVICE:
759                {
760                    Log.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
761                        +" Restart IBluetooth service");
762                    /* Enable without persisting the setting as
763                     it doesnt change when IBluetooth
764                     service restarts */
765                    mEnable = true;
766                    handleEnable(false, mQuietEnable);
767                    break;
768                }
769
770                case MESSAGE_TIMEOUT_UNBIND:
771                {
772                    Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
773                    synchronized(mConnection) {
774                        mUnbinding = false;
775                    }
776                    break;
777                }
778
779                case MESSAGE_USER_SWITCHED:
780                {
781                    if (DBG) {
782                        Log.d(TAG, "MESSAGE_USER_SWITCHED");
783                    }
784                    mHandler.removeMessages(MESSAGE_USER_SWITCHED);
785                    /* disable and enable BT when detect a user switch */
786                    if (mEnable && mBluetooth != null) {
787                        synchronized (mConnection) {
788                            if (mBluetooth != null) {
789                                //Unregister callback object
790                                try {
791                                    mBluetooth.unregisterCallback(mBluetoothCallback);
792                                } catch (RemoteException re) {
793                                    Log.e(TAG, "Unable to unregister",re);
794                                }
795                            }
796                        }
797                        mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
798
799                        waitForOnOff(true, false);
800
801                        bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
802
803                        // disable
804                        handleDisable(false);
805
806                        waitForOnOff(false, true);
807
808                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
809                                                    BluetoothAdapter.STATE_OFF);
810                        mState = BluetoothAdapter.STATE_OFF;
811                        sendBluetoothServiceDownCallback();
812                        synchronized (mConnection) {
813                            if (mBluetooth != null) {
814                                mBluetooth = null;
815                                //Unbind
816                                mContext.unbindService(mConnection);
817                            }
818                        }
819                        SystemClock.sleep(100);
820
821                        // enable
822                        handleEnable(false, mQuietEnable);
823		    } else if (mBinding || mBluetooth != null) {
824                        Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
825                        userMsg.arg2 = 1 + msg.arg2;
826                        // if user is switched when service is being binding
827                        // delay sending MESSAGE_USER_SWITCHED
828                        mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
829                        if (DBG) {
830                            Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
831                        }
832		    }
833                    break;
834                }
835            }
836        }
837    }
838
839    private void handleEnable(boolean persist, boolean quietMode) {
840        if (persist) {
841            persistBluetoothSetting(true);
842        }
843
844        mQuietEnable = quietMode;
845
846        synchronized(mConnection) {
847            if ((mBluetooth == null) && (!mBinding)) {
848                //Start bind timeout and bind
849                Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
850                mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
851                mConnection.setGetNameAddressOnly(false);
852                Intent i = new Intent(IBluetooth.class.getName());
853                if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
854                                          UserHandle.USER_CURRENT)) {
855                    mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
856                    Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
857                } else {
858                    mBinding = true;
859                }
860            } else if (mBluetooth != null) {
861                if (mConnection.isGetNameAddressOnly()) {
862                    // if GetNameAddressOnly is set, we can clear this flag,
863                    // so the service won't be unbind
864                    // after name and address are saved
865                    mConnection.setGetNameAddressOnly(false);
866                    //Register callback object
867                    try {
868                        mBluetooth.registerCallback(mBluetoothCallback);
869                    } catch (RemoteException re) {
870                        Log.e(TAG, "Unable to register BluetoothCallback",re);
871                    }
872                    //Inform BluetoothAdapter instances that service is up
873                    sendBluetoothServiceUpCallback();
874                }
875
876                //Check if name and address is loaded if not get it first.
877                if (!isNameAndAddressSet()) {
878                    try {
879                        if (DBG) Log.d(TAG,"Getting and storing Bluetooth name and address prior to enable.");
880                        storeNameAndAddress(mBluetooth.getName(),mBluetooth.getAddress());
881                    } catch (RemoteException e) {Log.e(TAG, "", e);};
882                }
883
884                //Enable bluetooth
885                try {
886                    if (!mQuietEnable) {
887                        if(!mBluetooth.enable()) {
888                            Log.e(TAG,"IBluetooth.enable() returned false");
889                        }
890                    }
891                    else {
892                        if(!mBluetooth.enableNoAutoConnect()) {
893                            Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
894                        }
895                    }
896                } catch (RemoteException e) {
897                    Log.e(TAG,"Unable to call enable()",e);
898                }
899            }
900        }
901    }
902
903    private void handleDisable(boolean persist) {
904        if (persist) {
905            persistBluetoothSetting(false);
906        }
907
908        synchronized(mConnection) {
909            // don't need to disable if GetNameAddressOnly is set,
910            // service will be unbinded after Name and Address are saved
911            if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
912                if (DBG) Log.d(TAG,"Sending off request.");
913
914                try {
915                    if(!mBluetooth.disable()) {
916                        Log.e(TAG,"IBluetooth.disable() returned false");
917                    }
918                } catch (RemoteException e) {
919                    Log.e(TAG,"Unable to call disable()",e);
920                }
921            }
922        }
923    }
924
925    private boolean checkIfCallerIsForegroundUser() {
926        int foregroundUser;
927        int callingUser = UserHandle.getCallingUserId();
928        long callingIdentity = Binder.clearCallingIdentity();
929        boolean valid = false;
930        try {
931            foregroundUser = ActivityManager.getCurrentUser();
932            valid = (callingUser == foregroundUser);
933            if (DBG) {
934                Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
935                    + " callingUser=" + callingUser
936                    + " foregroundUser=" + foregroundUser);
937            }
938        } finally {
939            Binder.restoreCallingIdentity(callingIdentity);
940        }
941        return valid;
942    }
943
944    private boolean enableHelper() {
945        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
946                                                "Need BLUETOOTH ADMIN permission");
947        if (DBG) {
948            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
949                    " mBinding = " + mBinding);
950        }
951
952        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
953        msg.arg1=1; //persist
954        msg.arg2=0; //No Quiet Mode
955        mHandler.sendMessage(msg);
956        return true;
957    }
958
959    private void bluetoothStateChangeHandler(int prevState, int newState) {
960        if (prevState != newState) {
961            //Notify all proxy objects first of adapter state change
962            if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
963                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
964                sendBluetoothStateCallback(isUp);
965
966                //If Bluetooth is off, send service down event to proxy objects, and unbind
967                if (!isUp) {
968                    //Only unbind with mEnable flag not set
969                    //For race condition: disable and enable back-to-back
970                    //Avoid unbind right after enable due to callback from disable
971                    if ((!mEnable) && (mBluetooth != null)) {
972                        sendBluetoothServiceDownCallback();
973                        unbindAndFinish();
974                    }
975                }
976            }
977
978            //Send broadcast message to everyone else
979            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
980            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
981            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
982            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
983            if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
984            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
985                    BLUETOOTH_PERM);
986        }
987    }
988
989    /**
990     *  if on is true, wait for state become ON
991     *  if off is true, wait for state become OFF
992     *  if both on and off are false, wait for state not ON
993     */
994    private boolean waitForOnOff(boolean on, boolean off) {
995        int i = 0;
996        while (i < 10) {
997            synchronized(mConnection) {
998                try {
999                    if (mBluetooth == null) break;
1000                    if (on) {
1001                        if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
1002                    } else if (off) {
1003                        if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
1004		    } else {
1005                        if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
1006		    }
1007                } catch (RemoteException e) {
1008                    Log.e(TAG, "getState()", e);
1009                    break;
1010                }
1011            }
1012            if (on || off) {
1013                SystemClock.sleep(300);
1014	    } else {
1015                SystemClock.sleep(50);
1016	    }
1017            i++;
1018        }
1019        Log.e(TAG,"waitForOnOff time out");
1020        return false;
1021    }
1022}
1023