blob: 37790a34629530b47fb58ff57098baf30ace73cb [file] [log] [blame]
fredc0f420372012-04-12 00:02:00 -07001/*
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}