BluetoothManagerService.java revision 0f42037eb7b5118015c2caca635538324ccf0ccf
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;
12
13import android.content.BroadcastReceiver;
14import android.content.ComponentName;
15import android.content.ContentResolver;
16import android.content.Context;
17import android.content.Intent;
18import android.content.IntentFilter;
19import android.content.ServiceConnection;
20import android.os.Handler;
21
22import android.os.IBinder;
23import android.os.Message;
24import android.os.RemoteException;
25import android.provider.Settings;
26import android.util.Log;
27import java.util.List;
28import java.util.ArrayList;
29
30class BluetoothManagerService extends IBluetoothManager.Stub {
31    private static final String TAG = "BluetoothManagerService";
32    private static final boolean DBG = true;
33
34    private static final boolean ALWAYS_SYNC_NAME_ADDRESS=true; //If true, always load name and address
35
36    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
37    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
38
39    private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED";
40    private static final String EXTRA_ACTION="action";
41
42    private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address";
43    private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
44
45    private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
46    private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
47
48    private static final int MESSAGE_ENABLE = 1;
49    private static final int MESSAGE_DISABLE = 2;
50    private static final int MESSAGE_REGISTER_ADAPTER = 3;
51    private static final int MESSAGE_UNREGISTER_ADAPTER = 4;
52    private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 5;
53    private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 6;
54    private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 11;
55    private static final int MESSAGE_BLUETOOTH_ON = 12;
56    private static final int MESSAGE_BLUETOOTH_OFF = 14;
57    private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 15;
58    private static final int MESSAGE_TIMEOUT_BIND =100;
59    private static final int MESSAGE_TIMEOUT_UNBIND =101;
60    private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
61    private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
62    private static final int MAX_SAVE_RETRIES=3;
63
64    private final Context mContext;
65    private String mAddress;
66    private String mName;
67    private ContentResolver mContentResolver;
68    private List<IBluetoothManagerCallback> mCallbacks;
69    private List<IBluetoothStateChangeCallback> mStateChangeCallbacks;
70
71    IntentFilter mFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
72    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
73
74        @Override
75        public void onReceive(Context context, Intent intent) {
76            String action = intent.getAction();
77            if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
78                int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
79                if (state == BluetoothAdapter.STATE_OFF) {
80                    Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_OFF);
81                    mHandler.sendMessage(msg);
82                } else if (state == BluetoothAdapter.STATE_ON) {
83                    Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_ON);
84                    mHandler.sendMessage(msg);
85                }
86            } else if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
87                String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
88                Log.d(TAG, "Bluetooth Adapter name changed to " + newName);
89                if (newName != null) {
90                    storeNameAndAddress(newName, null);
91                }
92            }
93        }
94    };
95
96    BluetoothManagerService(Context context) {
97        mContext = context;
98        mBluetooth = null;
99        mBinding = false;
100        mUnbinding = false;
101        mAddress = null;
102        mName = null;
103        mContentResolver = context.getContentResolver();
104        mCallbacks = new ArrayList<IBluetoothManagerCallback>();
105        mStateChangeCallbacks = new ArrayList<IBluetoothStateChangeCallback>();
106        mContext.registerReceiver(mReceiver, mFilter);
107
108        int airplaneModeOn = Settings.System.getInt(mContentResolver,
109                                                    Settings.System.AIRPLANE_MODE_ON, 0);
110        int bluetoothOn = Settings.Secure.getInt(mContentResolver,
111                                                 Settings.Secure.BLUETOOTH_ON, 0);
112        if (DBG) Log.d(TAG, "airplane mode: " + airplaneModeOn + " bluetoothOn: " + bluetoothOn);
113
114        loadStoredNameAndAddress();
115        if (airplaneModeOn == 0 &&  bluetoothOn!= 0) {
116            //Enable
117            if (DBG) Log.d(TAG, "Autoenabling Bluetooth.");
118            enable();
119        } else if (ALWAYS_SYNC_NAME_ADDRESS || !isNameAndAddressSet()) {
120            if (DBG) Log.d(TAG,"Retrieving name and address...");
121            getNameAndAddress();
122        }
123    }
124
125    private boolean isNameAndAddressSet() {
126        return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0;
127    }
128
129    private void loadStoredNameAndAddress() {
130        if (DBG) Log.d(TAG, "Loading stored name and address");
131        mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
132        mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
133        if (mName == null || mAddress == null) {
134            if (DBG) Log.d(TAG, "Name or address not cached...");
135        }
136    }
137
138    private void storeNameAndAddress(String name, String address) {
139        if (name != null) {
140            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
141            if (DBG) Log.d(TAG,"Stored name: " + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
142            mName = name;
143        }
144
145        if (address != null) {
146            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
147            if (DBG)  Log.d(TAG,"Stored address: " + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
148            mAddress=address;
149        }
150    }
151
152    public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
153        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
154                                                "Need BLUETOOTH permission");
155        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
156        msg.obj = callback;
157        mHandler.sendMessage(msg);
158        synchronized(mConnection) {
159            return mBluetooth;
160        }
161    }
162
163    public void unregisterAdapter(IBluetoothManagerCallback callback) {
164        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
165                                                "Need BLUETOOTH permission");
166        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
167        msg.obj = callback;
168        mHandler.sendMessage(msg);
169    }
170
171    public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
172        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
173                                                "Need BLUETOOTH permission");
174        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
175        msg.obj = callback;
176        mHandler.sendMessage(msg);
177    }
178
179    public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
180        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
181                                                "Need BLUETOOTH permission");
182        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
183        msg.obj = callback;
184        mHandler.sendMessage(msg);
185    }
186
187    public boolean isEnabled() {
188        synchronized(mConnection) {
189            try {
190                return (mBluetooth != null && mBluetooth.isEnabled());
191            } catch (RemoteException e) {
192                Log.e(TAG, "isEnabled()", e);
193            }
194        }
195        return false;
196    }
197
198    public void getNameAndAddress() {
199        synchronized(mConnection) {
200            if (mBinding) return ;
201            mBinding = true;
202        }
203        Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
204        mHandler.sendMessage(msg);
205    }
206
207    public boolean enable() {
208        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
209                                                "Need BLUETOOTH ADMIN permission");
210        synchronized(mConnection) {
211            //if (mBluetooth != null) return false; [fc] always allow an enable() to occur.
212            //If service is bound, we should not assume that bluetooth is enabled. What if
213            //Bluetooth never turned on?
214            if (mBinding) return true;
215            mBinding = true;
216        }
217        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
218        //msg.obj = new Boolean(true);
219        mHandler.sendMessage(msg);
220        return true;
221    }
222
223    public boolean disable(boolean persist) {
224        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
225                                                "Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
226        synchronized(mConnection) {
227             if (mBluetooth == null) return false;
228            //if (mUnbinding) return true;
229            //mUnbinding = true;
230        }
231        Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
232        msg.obj = new Boolean(persist);
233        mHandler.sendMessage(msg);
234        return true;
235    }
236
237    public void unbindAndFinish(boolean sendStop) {
238        synchronized (mConnection) {
239            if (mUnbinding) return;
240            mUnbinding = true;
241            if (mIsConnected) {
242                if (sendStop) {
243                    if (DBG) Log.d(TAG,"Sending stop request.");
244                    Intent i = new Intent(IBluetooth.class.getName());
245                    i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
246                    i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_OFF);
247                    mContext.startService(i);
248                }
249                if (DBG) Log.d(TAG, "Sending unbind request.");
250                mContext.unbindService(mConnection);
251                mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED));
252            }
253        }
254    }
255
256    public String getAddress() {
257        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
258                                                "Need BLUETOOTH ADMIN permission");
259        return mAddress;
260    }
261    public String getName() {
262        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
263                                                "Need BLUETOOTH ADMIN permission");
264        return mName;
265    }
266
267    private IBluetooth mBluetooth;
268    private boolean mBinding;
269    private boolean mUnbinding;
270    public boolean mIsConnected;
271
272    private class BluetoothServiceConnection implements ServiceConnection {
273
274        private boolean mGetNameAddressOnly;
275
276        public void setGetNameAddressOnly(boolean getOnly) {
277            mGetNameAddressOnly = getOnly;
278        }
279
280        public boolean isGetNameAddressOnly() {
281            return mGetNameAddressOnly;
282        }
283
284        public void onServiceConnected(ComponentName className, IBinder service) {
285            if (DBG) Log.d(TAG, "Proxy object connected");
286            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
287            msg.obj = service;
288            mHandler.sendMessage(msg);
289        }
290
291        public void onServiceDisconnected(ComponentName className) {
292            if (DBG) Log.d(TAG, "Proxy object disconnected");
293            // Called if we unexpected disconnected.
294            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
295            mHandler.sendMessage(msg);
296        }
297    }
298
299    private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
300
301    private final Handler mHandler = new Handler() {
302        @Override
303        public void handleMessage(Message msg) {
304            if (DBG) Log.d (TAG, "Message: " + msg.what);
305
306            switch (msg.what) {
307                case MESSAGE_GET_NAME_AND_ADDRESS: {
308                    if (mBluetooth == null) {
309                        //Start bind request
310                        if (!mIsConnected) {
311                            if (DBG) Log.d(TAG, "Binding to service to get name and address");
312                            mConnection.setGetNameAddressOnly(true);
313                            //Start bind timeout and bind
314                            Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
315                            mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
316                            Intent i = new Intent(IBluetooth.class.getName());
317                            if (!mContext.bindService(i, mConnection,
318                                                  Context.BIND_AUTO_CREATE)) {
319                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
320                                Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
321                            }
322                        }
323                    } else {
324                        Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
325                        mHandler.sendMessage(saveMsg);
326                    }
327                }
328                break;
329                case MESSAGE_SAVE_NAME_AND_ADDRESS: {
330                    if (mBluetooth != null) {
331                        String name =  null;
332                        String address = null;
333                        try {
334                            name =  mBluetooth.getName();
335                            address = mBluetooth.getAddress();
336                        } catch (RemoteException re) {
337                            Log.e(TAG,"",re);
338                        }
339
340                        if (name != null && address != null) {
341                            storeNameAndAddress(name,address);
342                            unbindAndFinish(false);
343                        } else  {
344                            if (msg.arg1 < MAX_SAVE_RETRIES) {
345                                Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
346                                retryMsg.arg1= 1+msg.arg1;
347                                if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
348                                mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
349                            } else {
350                                Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
351                                unbindAndFinish(false);
352                            }
353                        }
354                    }
355                }
356                break;
357                case MESSAGE_ENABLE: {
358                    if (mBluetooth == null) {
359                        //Start bind request
360                        if (!mIsConnected) {
361                            //Start bind timeout and bind
362                            Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
363                            mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
364                            Intent i = new Intent(IBluetooth.class.getName());
365                            i.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
366                            i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_ON);
367                            mContext.startService(i);
368                            mConnection.setGetNameAddressOnly(false);
369                            if (!mContext.bindService(i, mConnection,
370                                                  Context.BIND_AUTO_CREATE)) {
371                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
372                                Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
373                            }
374                        }
375                    } else {
376                        //Check if name and address is loaded if not get it first.
377                        if (ALWAYS_SYNC_NAME_ADDRESS || !isNameAndAddressSet()) {
378                            try {
379                                if (DBG) Log.d(TAG,"Bluetooth Proxy available: getting name and address prior to enable.");
380                                storeNameAndAddress(mBluetooth.getName(),mBluetooth.getAddress());
381                            } catch (RemoteException e) {Log.e(TAG, "", e);};
382                        }
383                        try {
384                            mBluetooth.enable();
385                        } catch (RemoteException e) {Log.e(TAG, "", e);};
386                    }
387                    // TODO(BT) what if service failed to start:
388                    // [fc] fixed: watch for bind timeout and handle accordingly
389                    // TODO(BT) persist the setting depending on argument
390                    // [fc]: let AdapterServiceHandle
391                }
392                break;
393                case MESSAGE_DISABLE:
394                    if (mBluetooth != null ) {
395                        boolean persist = (Boolean)msg.obj;
396                        try {
397                            mConnection.setGetNameAddressOnly(false);
398                            mBluetooth.disable(persist);
399                            //We will only unbind once we are sure that Bluetooth is OFFMESSAGE_TIMEOUT_UNBIND
400                            //mContext.unbindService(mConnection);
401                        } catch (RemoteException e) {
402                            Log.e(TAG, "Error disabling Bluetooth", e);
403                        }
404                    }
405
406                    // TODO(BT) what if service failed to stop:
407                    // [fc] fixed: watch for disable event and unbind accordingly
408                    // TODO(BT) persist the setting depending on argument
409                    // [fc]: let AdapterServiceHandle
410
411                    break;
412                case MESSAGE_REGISTER_ADAPTER:
413                {
414                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
415                    mCallbacks.add(callback);
416                }
417                    break;
418                case MESSAGE_UNREGISTER_ADAPTER:
419                {
420                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
421                    mCallbacks.remove(callback);
422                }
423                    break;
424                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
425                {
426                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
427                    mStateChangeCallbacks.add(callback);
428                }
429                    break;
430                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
431                {
432                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
433                    mStateChangeCallbacks.remove(callback);
434                }
435                    break;
436                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
437                {
438                    if (DBG) Log.d(TAG,"Bluetooth service connnected!");
439                    //Remove timeout
440                    mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
441
442                    IBinder service = (IBinder) msg.obj;
443                    synchronized(mConnection) {
444                        mIsConnected=true;
445                        mBinding = false;
446                        mBluetooth = IBluetooth.Stub.asInterface(service);
447                    }
448
449                    if (mConnection.isGetNameAddressOnly()) {
450                        //Request GET NAME AND ADDRESS
451                        Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
452                        mHandler.sendMessage(getMsg);
453                        return;
454                    }
455
456                    //Otherwise do the enable
457                    if (DBG) Log.d(TAG,"Requesting Bluetooth enable...");
458                    try {
459                        for (IBluetoothManagerCallback callback : mCallbacks) {
460                            callback.onBluetoothServiceUp(mBluetooth);
461                        }
462                    } catch (RemoteException e) {
463                        Log.e(TAG, "", e);
464                    }
465
466                    //Request Enable
467                    Message enableMsg = mHandler.obtainMessage(MESSAGE_ENABLE);
468                    //enableMsg.obj = new Boolean(false);
469                    mHandler.sendMessage(enableMsg);
470                }
471                break;
472                case MESSAGE_TIMEOUT_BIND:
473                {
474                    Log.e(TAG, "Timeout while trying to bind to Bluetooth Service");
475                    synchronized(mConnection) {
476                        mBinding = false;
477                    }
478                }
479                break;
480
481                case MESSAGE_BLUETOOTH_ON:
482                {
483                    if (DBG) Log.d(TAG, "Bluetooth is on!!!");
484                      try {
485                        for (IBluetoothStateChangeCallback callback : mStateChangeCallbacks) {
486                            callback.onBluetoothStateChange(true);
487                        }
488                    } catch (RemoteException e) {
489                        Log.e(TAG, "", e);
490                    }
491                }
492                    break;
493
494                case MESSAGE_BLUETOOTH_OFF:
495                {
496                    if (DBG) Log.d(TAG, "Bluetooth is off. Unbinding...");
497
498                    try {
499                        for (IBluetoothStateChangeCallback callback : mStateChangeCallbacks) {
500                            callback.onBluetoothStateChange(false);
501                        }
502                    } catch (RemoteException e) {
503                        Log.e(TAG, "", e);
504                    }
505                    unbindAndFinish(true);
506                }
507                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
508                {
509                    boolean isUnexpectedDisconnect = false;
510                    synchronized(mConnection) {
511                        mBluetooth = null;
512                        mIsConnected=false;
513                        if (mUnbinding) {
514                            mUnbinding = false;
515                        } else {
516                            isUnexpectedDisconnect = true;
517                        }
518                    }
519                    if (!isUnexpectedDisconnect &&!mConnection.isGetNameAddressOnly()) {
520                            if (DBG) Log.d(TAG,"Service finished unbinding. Calling callbacks...");
521                            try {
522                                for (IBluetoothManagerCallback callback : mCallbacks) {
523                                    callback.onBluetoothServiceDown();
524                                }
525                            }  catch (RemoteException e) {
526                                Log.e(TAG, "", e);
527                            }
528                    }
529                }
530                break;
531                case MESSAGE_TIMEOUT_UNBIND:
532                {
533                    Log.e(TAG, "Timeout while trying to unbind to Bluetooth Service");
534                    synchronized(mConnection) {
535                        mUnbinding = false;
536                    }
537                }
538                break;
539            }
540        }
541    };
542}
543