2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings.bluetooth;
19 import com.android.settings.R;
20 import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
21 import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener;
23 import android.app.AlertDialog;
24 import android.app.Notification;
25 import android.app.Service;
26 import android.bluetooth.BluetoothA2dp;
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.BluetoothDevice;
29 import android.bluetooth.BluetoothHeadset;
30 import android.content.Context;
31 import android.content.DialogInterface;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.SharedPreferences;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.IBinder;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.util.Log;
41 import android.view.LayoutInflater;
42 import android.view.View;
43 import android.view.WindowManager;
44 import android.widget.CheckBox;
45 import android.widget.CompoundButton;
47 import java.util.List;
50 public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener,
51 DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
52 CompoundButton.OnCheckedChangeListener, ServiceListener {
54 private static final String TAG = "DockService";
56 static final boolean DEBUG = false;
58 // Time allowed for the device to be undocked and redocked without severing
59 // the bluetooth connection
60 private static final long UNDOCKED_GRACE_PERIOD = 1000;
62 // Time allowed for the device to be undocked and redocked without turning
64 private static final long DISABLE_BT_GRACE_PERIOD = 2000;
66 // Msg for user wanting the UI to setup the dock
67 private static final int MSG_TYPE_SHOW_UI = 111;
69 // Msg for device docked event
70 private static final int MSG_TYPE_DOCKED = 222;
72 // Msg for device undocked event
73 private static final int MSG_TYPE_UNDOCKED_TEMPORARY = 333;
75 // Msg for undocked command to be process after UNDOCKED_GRACE_PERIOD millis
76 // since MSG_TYPE_UNDOCKED_TEMPORARY
77 private static final int MSG_TYPE_UNDOCKED_PERMANENT = 444;
79 // Msg for disabling bt after DISABLE_BT_GRACE_PERIOD millis since
80 // MSG_TYPE_UNDOCKED_PERMANENT
81 private static final int MSG_TYPE_DISABLE_BT = 555;
83 private static final String SHARED_PREFERENCES_NAME = "dock_settings";
85 private static final String SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED =
86 "disable_bt_when_undock";
88 private static final String SHARED_PREFERENCES_KEY_DISABLE_BT =
91 private static final String SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT =
92 "connect_retry_count";
95 * If disconnected unexpectedly, reconnect up to 6 times. Each profile counts
96 * as one time so it's only 3 times for both profiles on the car dock.
98 private static final int MAX_CONNECT_RETRY = 6;
100 private static final int INVALID_STARTID = -100;
102 // Created in OnCreate()
103 private volatile Looper mServiceLooper;
104 private volatile ServiceHandler mServiceHandler;
105 private Runnable mRunnable;
106 private DockService mContext;
107 private LocalBluetoothManager mBtManager;
109 // Normally set after getting a docked event and unset when the connection
110 // is severed. One exception is that mDevice could be null if the service
111 // was started after the docked event.
112 private BluetoothDevice mDevice;
114 // Created and used for the duration of the dialog
115 private AlertDialog mDialog;
116 private Profile[] mProfiles;
117 private boolean[] mCheckedItems;
118 private int mStartIdAssociatedWithDialog;
120 // Set while BT is being enabled.
121 private BluetoothDevice mPendingDevice;
122 private int mPendingStartId;
123 private int mPendingTurnOnStartId = INVALID_STARTID;
124 private int mPendingTurnOffStartId = INVALID_STARTID;
127 public void onCreate() {
128 if (DEBUG) Log.d(TAG, "onCreate");
130 mBtManager = LocalBluetoothManager.getInstance(this);
133 HandlerThread thread = new HandlerThread("DockService");
136 mServiceLooper = thread.getLooper();
137 mServiceHandler = new ServiceHandler(mServiceLooper);
141 public void onDestroy() {
142 if (DEBUG) Log.d(TAG, "onDestroy");
144 LocalBluetoothProfileManager.removeServiceListener(this);
145 if (mDialog != null) {
149 mServiceLooper.quit();
153 public IBinder onBind(Intent intent) {
159 public int onStartCommand(Intent intent, int flags, int startId) {
160 if (DEBUG) Log.d(TAG, "onStartCommand startId:" + startId + " flags: " + flags);
162 if (intent == null) {
163 // Nothing to process, stop.
164 if (DEBUG) Log.d(TAG, "START_NOT_STICKY - intent is null.");
166 // NOTE: We MUST not call stopSelf() directly, since we need to
167 // make sure the wake lock acquired by the Receiver is released.
168 DockEventReceiver.finishStartingService(this, startId);
169 return START_NOT_STICKY;
172 if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
173 handleBtStateChange(intent, startId);
174 return START_NOT_STICKY;
178 * This assumes that the intent sender has checked that this is a dock
179 * and that the intent is for a disconnect
181 if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) {
182 BluetoothDevice disconnectedDevice = intent
183 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
185 int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
186 if (retryCount < MAX_CONNECT_RETRY) {
187 setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
188 handleUnexpectedDisconnect(disconnectedDevice, Profile.HEADSET, startId);
190 return START_NOT_STICKY;
191 } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) {
192 BluetoothDevice disconnectedDevice = intent
193 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
195 int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
196 if (retryCount < MAX_CONNECT_RETRY) {
197 setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
198 handleUnexpectedDisconnect(disconnectedDevice, Profile.A2DP, startId);
200 return START_NOT_STICKY;
203 Message msg = parseIntent(intent);
206 if (DEBUG) Log.d(TAG, "START_NOT_STICKY - Bad intent.");
207 DockEventReceiver.finishStartingService(this, startId);
208 return START_NOT_STICKY;
211 if (msg.what == MSG_TYPE_DOCKED) {
212 removeSetting(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT);
218 return START_NOT_STICKY;
221 private final class ServiceHandler extends Handler {
222 public ServiceHandler(Looper looper) {
227 public void handleMessage(Message msg) {
232 // This method gets messages from both onStartCommand and mServiceHandler/mServiceLooper
233 private synchronized void processMessage(Message msg) {
234 int msgType = msg.what;
235 final int state = msg.arg1;
236 final int startId = msg.arg2;
237 boolean deferFinishCall = false;
238 BluetoothDevice device = null;
239 if (msg.obj != null) {
240 device = (BluetoothDevice) msg.obj;
243 if(DEBUG) Log.d(TAG, "processMessage: " + msgType + " state: " + state + " device = "
244 + (device == null ? "null" : device.toString()));
247 case MSG_TYPE_SHOW_UI:
248 if (mDialog != null) {
249 // Shouldn't normally happen
254 createDialog(mContext, mDevice, state, startId);
257 case MSG_TYPE_DOCKED:
259 // TODO figure out why hasMsg always returns false if device
261 Log.d(TAG, "1 Has undock perm msg = "
262 + mServiceHandler.hasMessages(MSG_TYPE_UNDOCKED_PERMANENT, mDevice));
263 Log.d(TAG, "2 Has undock perm msg = "
264 + mServiceHandler.hasMessages(MSG_TYPE_UNDOCKED_PERMANENT, device));
267 mServiceHandler.removeMessages(MSG_TYPE_UNDOCKED_PERMANENT);
268 mServiceHandler.removeMessages(MSG_TYPE_DISABLE_BT);
269 removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
271 if (!device.equals(mDevice)) {
272 if (mDevice != null) {
273 // Not expected. Cleanup/undock existing
274 handleUndocked(mContext, mBtManager, mDevice);
279 // Register first in case LocalBluetoothProfileManager
280 // becomes ready after isManagerReady is called and it
281 // would be too late to register a service listener.
282 LocalBluetoothProfileManager.addServiceListener(this);
283 if (LocalBluetoothProfileManager.isManagerReady()) {
284 handleDocked(device, state, startId);
285 // Not needed after all
286 LocalBluetoothProfileManager.removeServiceListener(this);
288 final BluetoothDevice d = device;
289 mRunnable = new Runnable() {
291 handleDocked(d, state, startId);
294 deferFinishCall = true;
299 case MSG_TYPE_UNDOCKED_PERMANENT:
300 // Grace period passed. Disconnect.
301 handleUndocked(mContext, mBtManager, device);
304 Log.d(TAG, "DISABLE_BT_WHEN_UNDOCKED = "
305 + getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
308 if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED)) {
309 // BT was disabled when we first docked
310 if (!hasOtherConnectedDevices(device)) {
311 if(DEBUG) Log.d(TAG, "QUEUED BT DISABLE");
312 // Queue a delayed msg to disable BT
313 Message newMsg = mServiceHandler.obtainMessage(MSG_TYPE_DISABLE_BT, 0,
315 mServiceHandler.sendMessageDelayed(newMsg, DISABLE_BT_GRACE_PERIOD);
316 deferFinishCall = true;
318 // Don't disable BT if something is connected
319 removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
324 case MSG_TYPE_UNDOCKED_TEMPORARY:
325 // Undocked event received. Queue a delayed msg to sever connection
326 Message newMsg = mServiceHandler.obtainMessage(MSG_TYPE_UNDOCKED_PERMANENT, state,
328 mServiceHandler.sendMessageDelayed(newMsg, UNDOCKED_GRACE_PERIOD);
331 case MSG_TYPE_DISABLE_BT:
332 if(DEBUG) Log.d(TAG, "BT DISABLE");
333 if (mBtManager.getBluetoothAdapter().disable()) {
334 removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
336 // disable() returned an error. Persist a flag to disable BT later
337 setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT, true);
338 mPendingTurnOffStartId = startId;
339 deferFinishCall = true;
340 if(DEBUG) Log.d(TAG, "disable failed. try again later " + startId);
345 if (mDialog == null && mPendingDevice == null && msgType != MSG_TYPE_UNDOCKED_TEMPORARY
346 && !deferFinishCall) {
347 // NOTE: We MUST not call stopSelf() directly, since we need to
348 // make sure the wake lock acquired by the Receiver is released.
349 DockEventReceiver.finishStartingService(DockService.this, startId);
353 public synchronized boolean hasOtherConnectedDevices(BluetoothDevice dock) {
354 List<CachedBluetoothDevice> cachedDevices = mBtManager.getCachedDeviceManager()
355 .getCachedDevicesCopy();
356 Set<BluetoothDevice> btDevices = mBtManager.getBluetoothAdapter().getBondedDevices();
357 if (btDevices == null || cachedDevices == null || btDevices.size() == 0) {
360 if(DEBUG) Log.d(TAG, "btDevices = " + btDevices.size());
361 if(DEBUG) Log.d(TAG, "cachedDevices = " + cachedDevices.size());
363 for (CachedBluetoothDevice device : cachedDevices) {
364 BluetoothDevice btDevice = device.getDevice();
365 if (!btDevice.equals(dock) && btDevices.contains(btDevice) && device.isConnected()) {
366 if(DEBUG) Log.d(TAG, "connected device = " + device.getName());
373 private Message parseIntent(Intent intent) {
374 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
375 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, -1234);
378 Log.d(TAG, "Action: " + intent.getAction() + " State:" + state
379 + " Device: " + (device == null ? "null" : device.getName()));
382 if (device == null) {
383 Log.w(TAG, "device is null");
389 case Intent.EXTRA_DOCK_STATE_UNDOCKED:
390 msgType = MSG_TYPE_UNDOCKED_TEMPORARY;
392 case Intent.EXTRA_DOCK_STATE_DESK:
393 case Intent.EXTRA_DOCK_STATE_CAR:
394 if (DockEventReceiver.ACTION_DOCK_SHOW_UI.equals(intent.getAction())) {
395 msgType = MSG_TYPE_SHOW_UI;
397 msgType = MSG_TYPE_DOCKED;
404 return mServiceHandler.obtainMessage(msgType, state, 0, device);
407 private boolean createDialog(DockService service, BluetoothDevice device, int state,
410 case Intent.EXTRA_DOCK_STATE_CAR:
411 case Intent.EXTRA_DOCK_STATE_DESK:
417 startForeground(0, new Notification());
419 // Device in a new dock.
420 boolean firstTime = !mBtManager.hasDockAutoConnectSetting(device.getAddress());
422 CharSequence[] items = initBtSettings(service, device, state, firstTime);
424 final AlertDialog.Builder ab = new AlertDialog.Builder(service);
425 ab.setTitle(service.getString(R.string.bluetooth_dock_settings_title));
428 ab.setMultiChoiceItems(items, mCheckedItems, service);
430 // Remember this settings
431 LayoutInflater inflater = (LayoutInflater) service
432 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
433 float pixelScaleFactor = service.getResources().getDisplayMetrics().density;
434 View view = inflater.inflate(R.layout.remember_dock_setting, null);
435 CheckBox rememberCheckbox = (CheckBox) view.findViewById(R.id.remember);
437 // check "Remember setting" by default if no value was saved
438 boolean checked = firstTime || mBtManager.getDockAutoConnectSetting(device.getAddress());
439 rememberCheckbox.setChecked(checked);
440 rememberCheckbox.setOnCheckedChangeListener(this);
441 int viewSpacingLeft = (int) (14 * pixelScaleFactor);
442 int viewSpacingRight = (int) (14 * pixelScaleFactor);
443 ab.setView(view, viewSpacingLeft, 0 /* top */, viewSpacingRight, 0 /* bottom */);
445 Log.d(TAG, "Auto connect = "
446 + mBtManager.getDockAutoConnectSetting(device.getAddress()));
450 ab.setPositiveButton(service.getString(android.R.string.ok), service);
452 mStartIdAssociatedWithDialog = startId;
453 mDialog = ab.create();
454 mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
455 mDialog.setOnDismissListener(service);
460 // Called when the individual bt profiles are clicked.
461 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
462 if (DEBUG) Log.d(TAG, "Item " + which + " changed to " + isChecked);
463 mCheckedItems[which] = isChecked;
466 // Called when the "Remember" Checkbox is clicked
467 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
468 if (DEBUG) Log.d(TAG, "onCheckedChanged: Remember Settings = " + isChecked);
469 if (mDevice != null) {
470 mBtManager.saveDockAutoConnectSetting(mDevice.getAddress(), isChecked);
474 // Called when the dialog is dismissed
475 public void onDismiss(DialogInterface dialog) {
476 // NOTE: We MUST not call stopSelf() directly, since we need to
477 // make sure the wake lock acquired by the Receiver is released.
478 if (mPendingDevice == null) {
479 DockEventReceiver.finishStartingService(mContext, mStartIdAssociatedWithDialog);
481 mContext.stopForeground(true);
484 // Called when clicked on the OK button
485 public void onClick(DialogInterface dialog, int which) {
486 if (which == DialogInterface.BUTTON_POSITIVE && mDevice != null) {
487 if (!mBtManager.hasDockAutoConnectSetting(mDevice.getAddress())) {
488 mBtManager.saveDockAutoConnectSetting(mDevice.getAddress(), true);
491 applyBtSettings(mDevice, mStartIdAssociatedWithDialog);
495 private CharSequence[] initBtSettings(DockService service, BluetoothDevice device, int state,
497 // TODO Avoid hardcoding dock and profiles. Read from system properties
498 int numOfProfiles = 0;
500 case Intent.EXTRA_DOCK_STATE_DESK:
503 case Intent.EXTRA_DOCK_STATE_CAR:
510 mProfiles = new Profile[numOfProfiles];
511 mCheckedItems = new boolean[numOfProfiles];
512 CharSequence[] items = new CharSequence[numOfProfiles];
515 case Intent.EXTRA_DOCK_STATE_CAR:
516 items[0] = service.getString(R.string.bluetooth_dock_settings_headset);
517 items[1] = service.getString(R.string.bluetooth_dock_settings_a2dp);
518 mProfiles[0] = Profile.HEADSET;
519 mProfiles[1] = Profile.A2DP;
521 // Enable by default for car dock
522 mCheckedItems[0] = true;
523 mCheckedItems[1] = true;
525 mCheckedItems[0] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
526 Profile.HEADSET).isPreferred(device);
527 mCheckedItems[1] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
528 Profile.A2DP).isPreferred(device);
532 case Intent.EXTRA_DOCK_STATE_DESK:
533 items[0] = service.getString(R.string.bluetooth_dock_settings_a2dp);
534 mProfiles[0] = Profile.A2DP;
536 // Disable by default for desk dock
537 mCheckedItems[0] = false;
539 mCheckedItems[0] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
540 Profile.A2DP).isPreferred(device);
547 private void handleBtStateChange(Intent intent, int startId) {
548 int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
549 synchronized (this) {
550 if(DEBUG) Log.d(TAG, "BtState = " + btState + " mPendingDevice = " + mPendingDevice);
551 if (btState == BluetoothAdapter.STATE_ON) {
552 if (mPendingDevice != null) {
553 if (mPendingDevice.equals(mDevice)) {
554 if(DEBUG) Log.d(TAG, "applying settings");
555 applyBtSettings(mPendingDevice, mPendingStartId);
557 Log.d(TAG, "mPendingDevice (" + mPendingDevice + ") != mDevice ("
561 mPendingDevice = null;
562 DockEventReceiver.finishStartingService(mContext, mPendingStartId);
565 Log.d(TAG, "A DISABLE_BT_WHEN_UNDOCKED = "
566 + getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
568 // Reconnect if docked and bluetooth was enabled by user.
569 Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
571 int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE,
572 Intent.EXTRA_DOCK_STATE_UNDOCKED);
573 if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
574 BluetoothDevice device = i
575 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
576 if (device != null) {
577 connectIfEnabled(device);
579 } else if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT)
580 && mBtManager.getBluetoothAdapter().disable()) {
581 mPendingTurnOffStartId = startId;
582 removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
588 if (mPendingTurnOnStartId != INVALID_STARTID) {
589 DockEventReceiver.finishStartingService(this, mPendingTurnOnStartId);
590 mPendingTurnOnStartId = INVALID_STARTID;
593 DockEventReceiver.finishStartingService(this, startId);
594 } else if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
595 // Remove the flag to disable BT if someone is turning off bt.
596 // The rational is that:
597 // a) if BT is off at undock time, no work needs to be done
598 // b) if BT is on at undock time, the user wants it on.
599 removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
600 DockEventReceiver.finishStartingService(this, startId);
601 } else if (btState == BluetoothAdapter.STATE_OFF) {
602 // Bluetooth was turning off as we were trying to turn it on.
604 if(DEBUG) Log.d(TAG, "Bluetooth = OFF mPendingDevice = " + mPendingDevice);
606 if (mPendingTurnOffStartId != INVALID_STARTID) {
607 DockEventReceiver.finishStartingService(this, mPendingTurnOffStartId);
608 removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
609 mPendingTurnOffStartId = INVALID_STARTID;
612 if (mPendingDevice != null) {
613 mBtManager.getBluetoothAdapter().enable();
614 mPendingTurnOnStartId = startId;
616 DockEventReceiver.finishStartingService(this, startId);
622 private void handleUnexpectedDisconnect(BluetoothDevice disconnectedDevice, Profile profile,
624 synchronized (this) {
625 if (DEBUG) Log.d(TAG, "handling failed connect for " + disconnectedDevice);
627 // Reconnect if docked.
628 if (disconnectedDevice != null) {
629 // registerReceiver can't be called from a BroadcastReceiver
630 Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
632 int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE,
633 Intent.EXTRA_DOCK_STATE_UNDOCKED);
634 if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
635 BluetoothDevice dockedDevice = i
636 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
637 if (dockedDevice != null && dockedDevice.equals(disconnectedDevice)) {
638 CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext,
639 mBtManager, dockedDevice);
640 cachedDevice.connect(profile);
646 DockEventReceiver.finishStartingService(this, startId);
650 private synchronized void connectIfEnabled(BluetoothDevice device) {
651 CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager, device);
652 List<Profile> profiles = cachedDevice.getConnectableProfiles();
653 for (int i = 0; i < profiles.size(); i++) {
654 LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
655 .getProfileManager(mBtManager, profiles.get(i));
657 if (Profile.A2DP == profiles.get(i)) {
658 auto = BluetoothA2dp.PRIORITY_AUTO_CONNECT;
659 } else if (Profile.HEADSET == profiles.get(i)) {
660 auto = BluetoothHeadset.PRIORITY_AUTO_CONNECT;
665 if (profileManager.getPreferred(device) == auto) {
666 cachedDevice.connect();
672 private synchronized void applyBtSettings(final BluetoothDevice device, int startId) {
673 if (device == null || mProfiles == null || mCheckedItems == null)
676 // Turn on BT if something is enabled
677 synchronized (this) {
678 for (boolean enable : mCheckedItems) {
680 int btState = mBtManager.getBluetoothState();
681 if(DEBUG) Log.d(TAG, "BtState = " + btState);
682 // May have race condition as the phone comes in and out and in the dock.
684 mBtManager.getBluetoothAdapter().enable();
687 case BluetoothAdapter.STATE_OFF:
688 case BluetoothAdapter.STATE_TURNING_OFF:
689 case BluetoothAdapter.STATE_TURNING_ON:
690 if (mPendingDevice != null && mPendingDevice.equals(mDevice)) {
694 mPendingDevice = device;
695 mPendingStartId = startId;
696 if (btState != BluetoothAdapter.STATE_TURNING_ON) {
697 setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED,
706 mPendingDevice = null;
708 boolean callConnect = false;
709 CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager,
711 for (int i = 0; i < mProfiles.length; i++) {
712 LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
713 .getProfileManager(mBtManager, mProfiles[i]);
715 if (DEBUG) Log.d(TAG, mProfiles[i].toString() + " = " + mCheckedItems[i]);
717 if (mCheckedItems[i]) {
718 // Checked but not connected
720 } else if (!mCheckedItems[i]) {
721 // Unchecked, may or may not be connected.
722 int status = profileManager.getConnectionStatus(cachedDevice.getDevice());
723 if (SettingsBtStatus.isConnectionStatusConnected(status)) {
724 if (DEBUG) Log.d(TAG, "applyBtSettings - Disconnecting");
725 cachedDevice.disconnect(mProfiles[i]);
728 profileManager.setPreferred(device, mCheckedItems[i]);
730 if (mCheckedItems[i] != profileManager.isPreferred(device)) {
731 Log.e(TAG, "Can't save prefered value");
737 if (DEBUG) Log.d(TAG, "applyBtSettings - Connecting");
738 cachedDevice.connect();
742 private synchronized void handleDocked(final BluetoothDevice device, final int state,
744 if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
745 // Setting == auto connect
746 initBtSettings(mContext, device, state, false);
747 applyBtSettings(mDevice, startId);
749 createDialog(mContext, device, state, startId);
753 private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager,
754 BluetoothDevice device) {
756 LocalBluetoothProfileManager.removeServiceListener(this);
757 if (mDialog != null) {
762 mPendingDevice = null;
763 CachedBluetoothDevice cachedBluetoothDevice = getCachedBluetoothDevice(context,
764 localManager, device);
765 cachedBluetoothDevice.disconnect();
768 private static CachedBluetoothDevice getCachedBluetoothDevice(Context context,
769 LocalBluetoothManager localManager, BluetoothDevice device) {
770 CachedBluetoothDeviceManager cachedDeviceManager = localManager.getCachedDeviceManager();
771 CachedBluetoothDevice cachedBluetoothDevice = cachedDeviceManager.findDevice(device);
772 if (cachedBluetoothDevice == null) {
773 cachedBluetoothDevice = new CachedBluetoothDevice(context, device);
775 return cachedBluetoothDevice;
778 private boolean getSettingBool(String key) {
779 SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
780 Context.MODE_PRIVATE);
781 return sharedPref.getBoolean(key, false);
784 private int getSettingInt(String key, int defaultValue) {
785 SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
786 Context.MODE_PRIVATE);
787 return sharedPref.getInt(key, defaultValue);
790 private void setSettingBool(String key, boolean bool) {
791 SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME,
792 Context.MODE_PRIVATE).edit();
793 editor.putBoolean(key, bool);
797 private void setSettingInt(String key, int value) {
798 SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME,
799 Context.MODE_PRIVATE).edit();
800 editor.putInt(key, value);
804 private void removeSetting(String key) {
805 SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
806 Context.MODE_PRIVATE);
807 SharedPreferences.Editor editor = sharedPref.edit();
812 public synchronized void onServiceConnected() {
813 if (mRunnable != null) {
816 LocalBluetoothProfileManager.removeServiceListener(this);
820 public void onServiceDisconnected() {