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