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