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