b/2680057 Fixed a bug where bt won't connect when the phone is docked (if Settings wasn't running).
The fix was to wait for the Bluetooth Headset service to come up before grabbing the settings and connecting. Change-Id: I57affca2fe7d571c96cfeaf9ffe5439a0b02af45
This commit is contained in:
@@ -18,6 +18,7 @@ package com.android.settings.bluetooth;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
||||||
|
import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
@@ -48,7 +49,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener,
|
public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener,
|
||||||
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
|
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
|
||||||
CompoundButton.OnCheckedChangeListener {
|
CompoundButton.OnCheckedChangeListener, ServiceListener {
|
||||||
|
|
||||||
private static final String TAG = "DockService";
|
private static final String TAG = "DockService";
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
// Created in OnCreate()
|
// Created in OnCreate()
|
||||||
private volatile Looper mServiceLooper;
|
private volatile Looper mServiceLooper;
|
||||||
private volatile ServiceHandler mServiceHandler;
|
private volatile ServiceHandler mServiceHandler;
|
||||||
|
private Runnable mRunnable;
|
||||||
private DockService mContext;
|
private DockService mContext;
|
||||||
private LocalBluetoothManager mBtManager;
|
private LocalBluetoothManager mBtManager;
|
||||||
|
|
||||||
@@ -138,6 +140,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
if (DEBUG) Log.d(TAG, "onDestroy");
|
if (DEBUG) Log.d(TAG, "onDestroy");
|
||||||
|
mRunnable = null;
|
||||||
|
LocalBluetoothProfileManager.removeServiceListener(this);
|
||||||
if (mDialog != null) {
|
if (mDialog != null) {
|
||||||
mDialog.dismiss();
|
mDialog.dismiss();
|
||||||
mDialog = null;
|
mDialog = null;
|
||||||
@@ -228,8 +232,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
// This method gets messages from both onStartCommand and mServiceHandler/mServiceLooper
|
// This method gets messages from both onStartCommand and mServiceHandler/mServiceLooper
|
||||||
private synchronized void processMessage(Message msg) {
|
private synchronized void processMessage(Message msg) {
|
||||||
int msgType = msg.what;
|
int msgType = msg.what;
|
||||||
int state = msg.arg1;
|
final int state = msg.arg1;
|
||||||
int startId = msg.arg2;
|
final int startId = msg.arg2;
|
||||||
boolean deferFinishCall = false;
|
boolean deferFinishCall = false;
|
||||||
BluetoothDevice device = null;
|
BluetoothDevice device = null;
|
||||||
if (msg.obj != null) {
|
if (msg.obj != null) {
|
||||||
@@ -271,12 +275,23 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
mDevice = device;
|
mDevice = device;
|
||||||
if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
|
|
||||||
// Setting == auto connect
|
// Register first in case LocalBluetoothProfileManager
|
||||||
initBtSettings(mContext, device, state, false);
|
// becomes ready after isManagerReady is called and it
|
||||||
applyBtSettings(mDevice, startId);
|
// would be too late to register a service listener.
|
||||||
|
LocalBluetoothProfileManager.addServiceListener(this);
|
||||||
|
if (LocalBluetoothProfileManager.isManagerReady()) {
|
||||||
|
handleDocked(device, state, startId);
|
||||||
|
// Not needed after all
|
||||||
|
LocalBluetoothProfileManager.removeServiceListener(this);
|
||||||
} else {
|
} else {
|
||||||
createDialog(mContext, mDevice, state, startId);
|
final BluetoothDevice d = device;
|
||||||
|
mRunnable = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
handleDocked(d, state, startId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
deferFinishCall = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -721,8 +736,21 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized void handleDocked(final BluetoothDevice device, final int state,
|
||||||
|
final int startId) {
|
||||||
|
if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
|
||||||
|
// Setting == auto connect
|
||||||
|
initBtSettings(mContext, device, state, false);
|
||||||
|
applyBtSettings(mDevice, startId);
|
||||||
|
} else {
|
||||||
|
createDialog(mContext, device, state, startId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager,
|
private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager,
|
||||||
BluetoothDevice device) {
|
BluetoothDevice device) {
|
||||||
|
mRunnable = null;
|
||||||
|
LocalBluetoothProfileManager.removeServiceListener(this);
|
||||||
if (mDialog != null) {
|
if (mDialog != null) {
|
||||||
mDialog.dismiss();
|
mDialog.dismiss();
|
||||||
mDialog = null;
|
mDialog = null;
|
||||||
@@ -778,4 +806,15 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void onServiceConnected() {
|
||||||
|
if (mRunnable != null) {
|
||||||
|
mRunnable.run();
|
||||||
|
mRunnable = null;
|
||||||
|
LocalBluetoothProfileManager.removeServiceListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,8 @@ import android.util.Log;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -53,6 +55,29 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
BluetoothUuid.ObexObjectPush
|
BluetoothUuid.ObexObjectPush
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for notifying BluetoothHeadset IPC clients when they have
|
||||||
|
* been connected to the BluetoothHeadset service.
|
||||||
|
*/
|
||||||
|
public interface ServiceListener {
|
||||||
|
/**
|
||||||
|
* Called to notify the client when this proxy object has been
|
||||||
|
* connected to the BluetoothHeadset service. Clients must wait for
|
||||||
|
* this callback before making IPC calls on the BluetoothHeadset
|
||||||
|
* service.
|
||||||
|
*/
|
||||||
|
public void onServiceConnected();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to notify the client that this proxy object has been
|
||||||
|
* disconnected from the BluetoothHeadset service. Clients must not
|
||||||
|
* make IPC calls on the BluetoothHeadset service after this callback.
|
||||||
|
* This callback will currently only occur if the application hosting
|
||||||
|
* the BluetoothHeadset service, but may be called more often in future.
|
||||||
|
*/
|
||||||
|
public void onServiceDisconnected();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: close profiles when we're shutting down
|
// TODO: close profiles when we're shutting down
|
||||||
private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
|
private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
|
||||||
new HashMap<Profile, LocalBluetoothProfileManager>();
|
new HashMap<Profile, LocalBluetoothProfileManager>();
|
||||||
@@ -76,6 +101,26 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LinkedList<ServiceListener> mServiceListeners = new LinkedList<ServiceListener>();
|
||||||
|
|
||||||
|
public static void addServiceListener(ServiceListener l) {
|
||||||
|
mServiceListeners.add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeServiceListener(ServiceListener l) {
|
||||||
|
mServiceListeners.remove(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isManagerReady() {
|
||||||
|
// Getting just the headset profile is fine for now. Will need to deal with A2DP
|
||||||
|
// and others if they aren't always in a ready state.
|
||||||
|
LocalBluetoothProfileManager profileManager = sProfileMap.get(Profile.HEADSET);
|
||||||
|
if (profileManager == null) {
|
||||||
|
return sProfileMap.size() > 0;
|
||||||
|
}
|
||||||
|
return profileManager.isProfileReady();
|
||||||
|
}
|
||||||
|
|
||||||
public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
|
public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
|
||||||
Profile profile) {
|
Profile profile) {
|
||||||
// Note: This code assumes that "localManager" is same as the
|
// Note: This code assumes that "localManager" is same as the
|
||||||
@@ -144,6 +189,8 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
|
return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract boolean isProfileReady();
|
||||||
|
|
||||||
// TODO: int instead of enum
|
// TODO: int instead of enum
|
||||||
public enum Profile {
|
public enum Profile {
|
||||||
HEADSET(R.string.bluetooth_profile_headset),
|
HEADSET(R.string.bluetooth_profile_headset),
|
||||||
@@ -247,6 +294,11 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
|
return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,6 +308,7 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
implements BluetoothHeadset.ServiceListener {
|
implements BluetoothHeadset.ServiceListener {
|
||||||
private BluetoothHeadset mService;
|
private BluetoothHeadset mService;
|
||||||
private Handler mUiHandler = new Handler();
|
private Handler mUiHandler = new Handler();
|
||||||
|
private boolean profileReady = false;
|
||||||
|
|
||||||
public HeadsetProfileManager(LocalBluetoothManager localManager) {
|
public HeadsetProfileManager(LocalBluetoothManager localManager) {
|
||||||
super(localManager);
|
super(localManager);
|
||||||
@@ -263,6 +316,7 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceConnected() {
|
public void onServiceConnected() {
|
||||||
|
profileReady = true;
|
||||||
// This could be called on a non-UI thread, funnel to UI thread.
|
// This could be called on a non-UI thread, funnel to UI thread.
|
||||||
mUiHandler.post(new Runnable() {
|
mUiHandler.post(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -277,9 +331,28 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
BluetoothHeadset.STATE_CONNECTED);
|
BluetoothHeadset.STATE_CONNECTED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (mServiceListeners.size() > 0) {
|
||||||
|
Iterator<ServiceListener> it = mServiceListeners.iterator();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
it.next().onServiceConnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected() {
|
public void onServiceDisconnected() {
|
||||||
|
profileReady = false;
|
||||||
|
if (mServiceListeners.size() > 0) {
|
||||||
|
Iterator<ServiceListener> it = mServiceListeners.iterator();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
it.next().onServiceDisconnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return profileReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -423,6 +496,11 @@ public abstract class LocalBluetoothProfileManager {
|
|||||||
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int convertState(int oppState) {
|
public int convertState(int oppState) {
|
||||||
switch (oppState) {
|
switch (oppState) {
|
||||||
|
Reference in New Issue
Block a user