BluetoothManagerService.java revision 116d1d4696ac8e300c83dd8a95e20e67717ef520
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5package com.android.server;
6
7import android.bluetooth.BluetoothAdapter;
8import android.bluetooth.IBluetooth;
9import android.bluetooth.IBluetoothManager;
10import android.bluetooth.IBluetoothManagerCallback;
11import android.bluetooth.IBluetoothStateChangeCallback;
12import android.content.BroadcastReceiver;
13import android.content.ComponentName;
14import android.content.ContentResolver;
15import android.content.Context;
16import android.content.Intent;
17import android.content.IntentFilter;
18import android.content.ServiceConnection;
19import android.os.Handler;
20import android.os.IBinder;
21import android.os.Message;
22import android.os.RemoteException;
23import android.provider.Settings;
24import android.util.Log;
25import java.util.List;
26import java.util.ArrayList;
27
28class BluetoothManagerService extends IBluetoothManager.Stub {
29    private static final String TAG = "BluetoothManagerService";
30    private static final boolean DBG = true;
31
32    private static final boolean ALWAYS_SYNC_NAME_ADDRESS=true; //If true, always load name and address
33    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
34    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
35    private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED";
36    private static final String EXTRA_ACTION="action";
37    private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address";
38    private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
39    private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
40    private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
41
42    private static final int MESSAGE_ENABLE = 1;
43    private static final int MESSAGE_DISABLE = 2;
44    private static final int MESSAGE_AIRPLANE_MODE_OFF=10;
45    private static final int MESSAGE_AIRPLANE_MODE_ON=11;
46    private static final int MESSAGE_REGISTER_ADAPTER = 20;
47    private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
48    private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
49    private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
50    private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
51    private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
52    private static final int MESSAGE_BLUETOOTH_ON = 50;
53    private static final int MESSAGE_BLUETOOTH_OFF = 51;
54    private static final int MESSAGE_TIMEOUT_BIND =100;
55    private static final int MESSAGE_TIMEOUT_UNBIND =101;
56    private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
57    private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
58    private static final int MAX_SAVE_RETRIES=3;
59
60    private final Context mContext;
61    private String mAddress;
62    private String mName;
63    private ContentResolver mContentResolver;
64    private List<IBluetoothManagerCallback> mCallbacks;
65    private List<IBluetoothStateChangeCallback> mStateChangeCallbacks;
66    private IBluetooth mBluetooth;
67    private boolean mBinding;
68    private boolean mUnbinding;
69
70    private void registerForAirplaneMode(IntentFilter filter) {
71        final ContentResolver resolver = mContext.getContentResolver();
72        final String airplaneModeRadios = Settings.System.getString(resolver,
73                Settings.System.AIRPLANE_MODE_RADIOS);
74        final String toggleableRadios = Settings.System.getString(resolver,
75                Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
76        boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true :
77                airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
78        if (mIsAirplaneSensitive) {
79            filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
80        }
81    }
82
83    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
84        @Override
85        public void onReceive(Context context, Intent intent) {
86            String action = intent.getAction();
87            if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
88                int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
89                if (state == BluetoothAdapter.STATE_OFF) {
90                    Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_OFF);
91                    mHandler.sendMessage(msg);
92                } else if (state == BluetoothAdapter.STATE_ON) {
93                    Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_ON);
94                    mHandler.sendMessage(msg);
95                }
96            } else if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
97                String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
98                Log.d(TAG, "Bluetooth Adapter name changed to " + newName);
99                if (newName != null) {
100                    storeNameAndAddress(newName, null);
101                }
102            } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
103                if (isAirplaneModeOn()) {
104                    Message msg = mHandler.obtainMessage(MESSAGE_AIRPLANE_MODE_ON);
105                    msg.arg1=0;
106                    mHandler.sendMessage(msg);
107                } else {
108                    Message msg = mHandler.obtainMessage(MESSAGE_AIRPLANE_MODE_OFF);
109                    msg.arg1=0;
110                    mHandler.sendMessage(msg);
111                }
112            }
113        }
114    };
115
116    BluetoothManagerService(Context context) {
117        mContext = context;
118        mBluetooth = null;
119        mBinding = false;
120        mUnbinding = false;
121        mAddress = null;
122        mName = null;
123        mContentResolver = context.getContentResolver();
124        mCallbacks = new ArrayList<IBluetoothManagerCallback>();
125        mStateChangeCallbacks = new ArrayList<IBluetoothStateChangeCallback>();
126        IntentFilter mFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
127        mFilter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
128        registerForAirplaneMode(mFilter);
129        mContext.registerReceiver(mReceiver, mFilter);
130        boolean airplaneModeOn = isAirplaneModeOn();
131        boolean bluetoothOn = isBluetoothPersistedStateOn();
132        loadStoredNameAndAddress();
133        if (DBG) Log.d(TAG, "airplaneModeOn: " + airplaneModeOn + " bluetoothOn: " + bluetoothOn);
134        if (!airplaneModeOn &&  bluetoothOn) {
135            //Enable
136            if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
137            enable();
138        } else if (ALWAYS_SYNC_NAME_ADDRESS || !isNameAndAddressSet()) {
139            //Sync the Bluetooth name and address from the Bluetooth Adapter
140            if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
141            getNameAndAddress();
142        }
143    }
144
145    /**
146     *  Returns true if airplane mode is currently on
147     */
148    private final boolean isAirplaneModeOn() {
149        return Settings.System.getInt(mContext.getContentResolver(),
150                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
151    }
152
153    /**
154     *  Returns true if the Bluetooth saved state is "on"
155     */
156    private final boolean isBluetoothPersistedStateOn() {
157        return Settings.Secure.getInt(mContentResolver,
158                Settings.Secure.BLUETOOTH_ON, 0) ==1;
159    }
160
161    /**
162     *  Save the Bluetooth on/off state
163     *
164     */
165    private void persistBluetoothSetting(boolean setOn) {
166        Settings.Secure.putInt(mContext.getContentResolver(),
167                               Settings.Secure.BLUETOOTH_ON,
168                               setOn ? 1 : 0);
169    }
170
171    /**
172     * Returns true if the Bluetooth Adapter's name and address is
173     * locally cached
174     * @return
175     */
176    private boolean isNameAndAddressSet() {
177        return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0;
178    }
179
180    /**
181     * Retrieve the Bluetooth Adapter's name and address and save it in
182     * in the local cache
183     */
184    private void loadStoredNameAndAddress() {
185        if (DBG) Log.d(TAG, "Loading stored name and address");
186        mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
187        mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
188        if (mName == null || mAddress == null) {
189            if (DBG) Log.d(TAG, "Name or address not cached...");
190        }
191    }
192
193    /**
194     * Save the Bluetooth name and address in the persistent store.
195     * Only non-null values will be saved.
196     * @param name
197     * @param address
198     */
199    private void storeNameAndAddress(String name, String address) {
200        if (name != null) {
201            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
202            mName = name;
203            if (DBG) Log.d(TAG,"Stored Bluetooth name: " +
204                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
205        }
206
207        if (address != null) {
208            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
209            mAddress=address;
210            if (DBG)  Log.d(TAG,"Stored Bluetoothaddress: " +
211                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
212        }
213    }
214
215    public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
216        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
217                                                "Need BLUETOOTH permission");
218        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
219        msg.obj = callback;
220        mHandler.sendMessage(msg);
221        synchronized(mConnection) {
222            return mBluetooth;
223        }
224    }
225
226    public void unregisterAdapter(IBluetoothManagerCallback callback) {
227        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
228                                                "Need BLUETOOTH permission");
229        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
230        msg.obj = callback;
231        mHandler.sendMessage(msg);
232    }
233
234    public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
235        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
236                                                "Need BLUETOOTH permission");
237        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
238        msg.obj = callback;
239        mHandler.sendMessage(msg);
240    }
241
242    public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
243        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
244                                                "Need BLUETOOTH permission");
245        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
246        msg.obj = callback;
247        mHandler.sendMessage(msg);
248    }
249
250    public boolean isEnabled() {
251        synchronized(mConnection) {
252            try {
253                return (mBluetooth != null && mBluetooth.isEnabled());
254            } catch (RemoteException e) {
255                Log.e(TAG, "isEnabled()", e);
256            }
257        }
258        return false;
259    }
260
261    private boolean isConnected() {
262        return mBluetooth != null;
263    }
264
265    public void getNameAndAddress() {
266        if (DBG) {
267            Log.d(TAG,"getNameAndAddress(): mBluetooth = " +
268                (mBluetooth==null?"null":mBluetooth) +
269                " mBinding = " + mBinding +
270                " isConnected = " + isConnected());
271        }
272        synchronized(mConnection) {
273            if (mBinding) return ;
274            if (!isConnected()) mBinding = true;
275        }
276        Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
277        mHandler.sendMessage(msg);
278    }
279
280    public boolean enable() {
281        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
282                                                "Need BLUETOOTH ADMIN permission");
283        if (DBG) {
284            Log.d(TAG,"enable():  mBluetooth =" +
285                    (mBluetooth==null?"null":mBluetooth) +
286                    " mBinding = " + mBinding +
287                    " isConnected = " + isConnected());
288        }
289
290        synchronized(mConnection) {
291            //if (mBluetooth != null) return false;
292            if (mBinding) {
293                Log.w(TAG,"enable(): binding in progress. Returning..");
294                return true;
295            }
296            if (!isConnected()) mBinding = true;
297        }
298
299        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
300        msg.arg1=1; //persist
301        mHandler.sendMessage(msg);
302        return true;
303    }
304
305    public boolean disable(boolean persist) {
306        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
307                                                "Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
308        if (DBG) {
309            Log.d(TAG,"disable(): mBluetooth = " +
310                (mBluetooth==null?"null":mBluetooth) +
311                " mBinding = " + mBinding +
312                " isConnected = " + isConnected());}
313
314        synchronized(mConnection) {
315             if (mBluetooth == null) return false;
316        }
317        Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
318        msg.arg1=(persist?1:0);
319        mHandler.sendMessage(msg);
320        return true;
321    }
322
323    public void unbindAndFinish() {
324        if (DBG) {
325            Log.d(TAG,"unbindAndFinish(): " +
326                (mBluetooth==null?"null":mBluetooth) +
327                " mBinding = " + mBinding +
328                " isConnected = " + isConnected());
329        }
330
331        synchronized (mConnection) {
332            if (mUnbinding) return;
333            mUnbinding = true;
334            if (isConnected()) {
335                if (DBG) Log.d(TAG, "Sending unbind request.");
336                mContext.unbindService(mConnection);
337                mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED));
338            } else {
339                mUnbinding=false;
340            }
341        }
342    }
343
344    public String getAddress() {
345        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
346                                                "Need BLUETOOTH ADMIN permission");
347        synchronized(mConnection) {
348            if (mBluetooth != null) {
349                try {
350                    return mBluetooth.getAddress();
351                } catch (RemoteException e) {
352                    Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
353                }
354            }
355        }
356        return mAddress;
357    }
358
359    public String getName() {
360        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
361                                                "Need BLUETOOTH ADMIN permission");
362        synchronized(mConnection) {
363            if (mBluetooth != null) {
364                try {
365                    return mBluetooth.getName();
366                } catch (RemoteException e) {
367                    Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
368                }
369            }
370        }
371        return mName;
372    }
373
374    private class BluetoothServiceConnection implements ServiceConnection {
375
376        private boolean mGetNameAddressOnly;
377
378        public void setGetNameAddressOnly(boolean getOnly) {
379            mGetNameAddressOnly = getOnly;
380        }
381
382        public boolean isGetNameAddressOnly() {
383            return mGetNameAddressOnly;
384        }
385
386        public void onServiceConnected(ComponentName className, IBinder service) {
387            if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService");
388            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
389            msg.obj = service;
390            mHandler.sendMessage(msg);
391        }
392
393        public void onServiceDisconnected(ComponentName className) {
394            // Called if we unexpected disconnected.
395            if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService");
396            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
397            mHandler.sendMessage(msg);
398        }
399    }
400
401    private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
402
403    private final Handler mHandler = new Handler() {
404        @Override
405        public void handleMessage(Message msg) {
406            if (DBG) Log.d (TAG, "Message: " + msg.what);
407            switch (msg.what) {
408                case MESSAGE_GET_NAME_AND_ADDRESS: {
409                    if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
410                    if (mBluetooth == null) {
411                        //Start bind request
412                        if (!isConnected()) {
413                            if (DBG) Log.d(TAG, "Binding to service to get name and address");
414                            mConnection.setGetNameAddressOnly(true);
415                            //Start bind timeout and bind
416                            Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
417                            mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
418                            Intent i = new Intent(IBluetooth.class.getName());
419                            if (!mContext.bindService(i, mConnection,
420                                                  Context.BIND_AUTO_CREATE)) {
421                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
422                                Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
423                            }
424                        }
425                    } else {
426                        Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
427                        mHandler.sendMessage(saveMsg);
428                    }
429                }
430                    break;
431                case MESSAGE_SAVE_NAME_AND_ADDRESS: {
432                    if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS");
433                    if (mBluetooth != null) {
434                        String name =  null;
435                        String address = null;
436                        try {
437                            name =  mBluetooth.getName();
438                            address = mBluetooth.getAddress();
439                        } catch (RemoteException re) {
440                            Log.e(TAG,"",re);
441                        }
442
443                        if (name != null && address != null) {
444                            storeNameAndAddress(name,address);
445                            Intent i = new Intent(IBluetooth.class.getName());
446                            i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
447                            i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_OFF);
448                            mContext.startService(i);
449                            unbindAndFinish();
450                        } else {
451                            if (msg.arg1 < MAX_SAVE_RETRIES) {
452                                Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
453                                retryMsg.arg1= 1+msg.arg1;
454                                if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
455                                mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
456                            } else {
457                                Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
458                                unbindAndFinish();
459                            }
460                        }
461                    }
462                }
463                    break;
464                case MESSAGE_AIRPLANE_MODE_OFF: {
465                    if (DBG) Log.d(TAG,"MESSAGE_AIRPLANE_MODE_OFF");
466                    //Check if we should turn on bluetooth
467                    if (!isBluetoothPersistedStateOn()) {
468                        if (DBG)Log.d(TAG, "Bluetooth persisted state is off. Not turning on Bluetooth.");
469                        return;
470                    }
471                    //Fall through to MESSAGE_ENABLE
472                }
473                case MESSAGE_ENABLE: {
474                    if (DBG) {
475                        Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth +
476                            " isConnected = " + isConnected());
477                    }
478                    boolean persist =  (1==msg.arg1);
479                    if (persist) {
480                        persistBluetoothSetting(true);
481                    }
482                    if (mBluetooth == null) {
483                        //Start bind request
484                        if (!isConnected()) {
485                            //Start bind timeout and bind
486                            Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
487                            mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
488                            Intent i = new Intent(IBluetooth.class.getName());
489                            i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
490                            i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_ON);
491                            mContext.startService(i);
492                            mConnection.setGetNameAddressOnly(false);
493                            if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE)) {
494                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
495                                Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
496                            }
497                        }
498                    } else {
499                        //Check if name and address is loaded if not get it first.
500                        if (ALWAYS_SYNC_NAME_ADDRESS || !isNameAndAddressSet()) {
501                            try {
502                                if (DBG) Log.d(TAG,"Getting and storing Bluetooth name and address prior to enable.");
503                                storeNameAndAddress(mBluetooth.getName(),mBluetooth.getAddress());
504                            } catch (RemoteException e) {Log.e(TAG, "", e);};
505                        }
506                        Intent i = new Intent(IBluetooth.class.getName());
507                        i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
508                        i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_ON);
509                        mContext.startService(i);
510                    }
511                    // TODO(BT) what if service failed to start:
512                    // [fc] fixed: watch for bind timeout and handle accordingly
513                    // TODO(BT) persist the setting depending on argument
514                    // [fc]: let AdapterServiceHandle
515                }
516                    break;
517                case MESSAGE_AIRPLANE_MODE_ON:;
518                    if (DBG) {
519                        Log.d(TAG, "MESSAGE_AIRPLANE_MODE_ON: mBluetooth = " + mBluetooth +
520                                " isConnected = " + isConnected());
521                      //Fall through to MESSAGE_DISABLE
522                    }
523                case MESSAGE_DISABLE:
524                    if (mBluetooth != null ) {
525                        boolean persist =  (1==msg.arg1);
526                        if (persist) {
527                            persistBluetoothSetting(false);
528                        }
529                        mConnection.setGetNameAddressOnly(false);
530                        if (DBG) Log.d(TAG,"Sending off request.");
531                        Intent i = new Intent(IBluetooth.class.getName());
532                        i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
533                        i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_OFF);
534                        mContext.startService(i);
535                    }
536                    // TODO(BT) what if service failed to stop:
537                    // [fc] fixed: watch for disable event and unbind accordingly
538                    // TODO(BT) persist the setting depending on argument
539                    // [fc]: let AdapterServiceHandle
540
541                    break;
542                case MESSAGE_REGISTER_ADAPTER:
543                {
544                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
545                    mCallbacks.add(callback);
546                }
547                    break;
548                case MESSAGE_UNREGISTER_ADAPTER:
549                {
550                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
551                    mCallbacks.remove(callback);
552                }
553                    break;
554                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
555                {
556                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
557                    mStateChangeCallbacks.add(callback);
558                }
559                    break;
560                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
561                {
562                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
563                    mStateChangeCallbacks.remove(callback);
564                }
565                    break;
566                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
567                {
568                    if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED");
569
570                    //Remove timeout
571                    mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
572
573                    IBinder service = (IBinder) msg.obj;
574                    synchronized(mConnection) {
575                        mBinding = false;
576                        mBluetooth = IBluetooth.Stub.asInterface(service);
577                    }
578
579                    if (mConnection.isGetNameAddressOnly()) {
580                        //Request GET NAME AND ADDRESS
581                        Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
582                        mHandler.sendMessage(getMsg);
583                        return;
584                    }
585
586                    try {
587                        for (IBluetoothManagerCallback callback : mCallbacks) {
588                            callback.onBluetoothServiceUp(mBluetooth);
589                        }
590                    } catch (RemoteException e) {
591                        Log.e(TAG, "", e);
592                    }
593
594                }
595                    break;
596                case MESSAGE_TIMEOUT_BIND: {
597                    Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
598                    synchronized(mConnection) {
599                        mBinding = false;
600                    }
601                }
602                    break;
603                case MESSAGE_BLUETOOTH_ON:
604                {
605                    if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_ON");
606                    try {
607                        for (IBluetoothStateChangeCallback callback : mStateChangeCallbacks) {
608                            callback.onBluetoothStateChange(true);
609                        }
610                    } catch (RemoteException e) {
611                        Log.e(TAG, "", e);
612                    }
613                }
614                    break;
615                case MESSAGE_BLUETOOTH_OFF:
616                {
617                    if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_OFF");
618                    try {
619                        for (IBluetoothStateChangeCallback callback : mStateChangeCallbacks) {
620                            callback.onBluetoothStateChange(false);
621                        }
622                    } catch (RemoteException e) {
623                        Log.e(TAG, "", e);
624                    }
625                    unbindAndFinish();
626                }
627                    break;
628                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
629                {
630                    if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
631                    boolean isUnexpectedDisconnect = false;
632                    synchronized(mConnection) {
633                        mBluetooth = null;
634                        if (mUnbinding) {
635                            mUnbinding = false;
636                        } else {
637                            isUnexpectedDisconnect = true;
638                        }
639                    }
640                    if (!mConnection.isGetNameAddressOnly()) {
641                            if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: Calling onBluetoothSerivceDown callbacks");
642                            try {
643                                for (IBluetoothManagerCallback callback : mCallbacks) {
644                                    callback.onBluetoothServiceDown();
645                                }
646                            }  catch (RemoteException e) {
647                                Log.e(TAG, "", e);
648                            }
649                    }
650                }
651                    break;
652                case MESSAGE_TIMEOUT_UNBIND:
653                {
654                    Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
655                    synchronized(mConnection) {
656                        mUnbinding = false;
657                    }
658                }
659                    break;
660            }
661        }
662    };
663}
664