summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/bluetooth/BluetoothDeviceProfileState.java (renamed from core/java/android/bluetooth/BluetoothProfileConnectionState.java)46
-rw-r--r--core/java/android/bluetooth/BluetoothProfileState.java144
-rw-r--r--core/java/android/server/BluetoothService.java76
3 files changed, 227 insertions, 39 deletions
diff --git a/core/java/android/bluetooth/BluetoothProfileConnectionState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index a58b858..8e655e2 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnectionState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,8 +54,8 @@ import com.android.internal.util.HierarchicalStateMachine;
* Todo(): Write tests for this class, when the Android Mock support is completed.
* @hide
*/
-public final class BluetoothProfileConnectionState extends HierarchicalStateMachine {
- private static final String TAG = "BluetoothProfileConnectionState";
+public final class BluetoothDeviceProfileState extends HierarchicalStateMachine {
+ private static final String TAG = "BluetoothDeviceProfileState";
private static final boolean DBG = true; //STOPSHIP - Change to false
public static final int CONNECT_HFP_OUTGOING = 1;
@@ -72,7 +72,7 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
public static final int AUTO_CONNECT_PROFILES = 10;
public static final int TRANSITION_TO_STABLE = 11;
- private static final int AUTO_CONNECT_DELAY = 8000; // 8 secs
+ private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs
private BondedDevice mBondedDevice = new BondedDevice();
private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
@@ -137,7 +137,22 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
}
};
- public BluetoothProfileConnectionState(Context context, String address,
+ private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) {
+ // This works only because these broadcast intents are "sticky"
+ Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+ if (i != null) {
+ int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device != null && autoConnectDevice.equals(device)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public BluetoothDeviceProfileState(Context context, String address,
BluetoothService service, BluetoothA2dpService a2dpService) {
super(address);
mContext = context;
@@ -168,12 +183,12 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
mHeadsetService = new BluetoothHeadset(mContext, this);
}
public void onServiceConnected() {
- synchronized(BluetoothProfileConnectionState.this) {
+ synchronized(BluetoothDeviceProfileState.this) {
mHeadsetServiceConnected = true;
}
}
public void onServiceDisconnected() {
- synchronized(BluetoothProfileConnectionState.this) {
+ synchronized(BluetoothDeviceProfileState.this) {
mHeadsetServiceConnected = false;
}
}
@@ -222,16 +237,21 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
processCommand(UNPAIR);
break;
case AUTO_CONNECT_PROFILES:
- if (!mHeadsetServiceConnected) {
+ if (isPhoneDocked(mDevice)) {
+ // Don't auto connect to docks.
+ break;
+ } else if (!mHeadsetServiceConnected) {
deferMessage(message);
} else {
if (mHeadsetService.getPriority(mDevice) ==
- BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
+ BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
+ !mHeadsetService.isConnected(mDevice)) {
mHeadsetService.connectHeadset(mDevice);
}
if (mA2dpService != null &&
- mA2dpService.getSinkPriority(mDevice) ==
- BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
+ mA2dpService.getSinkPriority(mDevice) ==
+ BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
+ mA2dpService.getConnectedSinks().length == 0) {
mA2dpService.connectSink(mDevice);
}
}
@@ -633,6 +653,10 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
return false;
}
+ /*package*/ BluetoothDevice getDevice() {
+ return mDevice;
+ }
+
private void log(String message) {
if (DBG) {
Log.i(TAG, "Device:" + mDevice + " Message:" + message);
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
new file mode 100644
index 0000000..946dcaa
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+/**
+ * This state machine is used to serialize the connections
+ * to a particular profile. Currently, we only allow one device
+ * to be connected to a particular profile.
+ * States:
+ * {@link StableState} : No pending commands. Send the
+ * command to the appropriate remote device specific state machine.
+ *
+ * {@link PendingCommandState} : A profile connection / disconnection
+ * command is being executed. This will result in a profile state
+ * change. Defer all commands.
+ * @hide
+ */
+
+public class BluetoothProfileState extends HierarchicalStateMachine {
+ private static final boolean DBG = true; // STOPSHIP - change to false.
+ private static final String TAG = "BluetoothProfileState";
+
+ public static int HFP = 0;
+ public static int A2DP = 1;
+
+ private static int TRANSITION_TO_STABLE = 100;
+
+ private int mProfile;
+ private BluetoothDevice mPendingDevice;
+ private PendingCommandState mPendingCommandState = new PendingCommandState();
+ private StableState mStableState = new StableState();
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
+ if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
+ newState == BluetoothHeadset.STATE_DISCONNECTED)) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
+ if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED ||
+ newState == BluetoothA2dp.STATE_DISCONNECTED)) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ }
+ }
+ };
+
+ public BluetoothProfileState(Context context, int profile) {
+ super("BluetoothProfileState:" + profile);
+ mProfile = profile;
+ addState(mStableState);
+ addState(mPendingCommandState);
+ setInitialState(mStableState);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+ filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+ context.registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ private class StableState extends HierarchicalState {
+ @Override
+ protected void enter() {
+ log("Entering Stable State");
+ mPendingDevice = null;
+ }
+
+ @Override
+ protected boolean processMessage(Message msg) {
+ if (msg.what != TRANSITION_TO_STABLE) {
+ transitionTo(mPendingCommandState);
+ }
+ return true;
+ }
+ }
+
+ private class PendingCommandState extends HierarchicalState {
+ @Override
+ protected void enter() {
+ log("Entering PendingCommandState State");
+ dispatchMessage(getCurrentMessage());
+ }
+
+ @Override
+ protected boolean processMessage(Message msg) {
+ if (msg.what == TRANSITION_TO_STABLE) {
+ transitionTo(mStableState);
+ } else {
+ dispatchMessage(msg);
+ }
+ return true;
+ }
+
+ private void dispatchMessage(Message msg) {
+ BluetoothDeviceProfileState deviceProfileMgr =
+ (BluetoothDeviceProfileState)msg.obj;
+ int cmd = msg.arg1;
+ if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) {
+ mPendingDevice = deviceProfileMgr.getDevice();
+ deviceProfileMgr.sendMessage(cmd);
+ } else {
+ Message deferMsg = new Message();
+ deferMsg.arg1 = cmd;
+ deferMsg.obj = deviceProfileMgr;
+ deferMessage(deferMsg);
+ }
+ }
+ }
+
+ private void log(String message) {
+ if (DBG) {
+ Log.i(TAG, "Message:" + message);
+ }
+ }
+}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 6720145..31e5a7b 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -28,7 +28,8 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothProfileConnectionState;
+import android.bluetooth.BluetoothDeviceProfileState;
+import android.bluetooth.BluetoothProfileState;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetooth;
@@ -123,7 +124,9 @@ public class BluetoothService extends IBluetooth.Stub {
private final HashMap<Integer, Integer> mServiceRecordToPid;
- private final HashMap<String, BluetoothProfileConnectionState> mProfileConnectionMgr;
+ private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
+ private final BluetoothProfileState mA2dpProfileState;
+ private final BluetoothProfileState mHfpProfileState;
private BluetoothA2dpService mA2dpService;
private static String mDockAddress;
@@ -183,7 +186,12 @@ public class BluetoothService extends IBluetooth.Stub {
mUuidIntentTracker = new ArrayList<String>();
mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
mServiceRecordToPid = new HashMap<Integer, Integer>();
- mProfileConnectionMgr = new HashMap<String, BluetoothProfileConnectionState>();
+ mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
+ mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
+ mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
+
+ mHfpProfileState.start();
+ mA2dpProfileState.start();
IntentFilter filter = new IntentFilter();
registerForAirplaneMode(filter);
@@ -1179,9 +1187,9 @@ public class BluetoothService extends IBluetooth.Stub {
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
return false;
}
- BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
- state.sendMessage(BluetoothProfileConnectionState.UNPAIR);
+ state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
return true;
} else {
return false;
@@ -1942,53 +1950,65 @@ public class BluetoothService extends IBluetooth.Stub {
}
public boolean connectHeadset(String address) {
- BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
- state.sendMessage(BluetoothProfileConnectionState.CONNECT_HFP_OUTGOING);
- return true;
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
+ msg.obj = state;
+ mHfpProfileState.sendMessage(msg);
+ return true;
}
return false;
}
public boolean disconnectHeadset(String address) {
- BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
- state.sendMessage(BluetoothProfileConnectionState.DISCONNECT_HFP_OUTGOING);
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
+ msg.obj = state;
+ mHfpProfileState.sendMessage(msg);
return true;
}
return false;
}
public boolean connectSink(String address) {
- BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
- state.sendMessage(BluetoothProfileConnectionState.CONNECT_A2DP_OUTGOING);
- return true;
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
+ msg.obj = state;
+ mA2dpProfileState.sendMessage(msg);
+ return true;
}
return false;
}
public boolean disconnectSink(String address) {
- BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
- state.sendMessage(BluetoothProfileConnectionState.DISCONNECT_A2DP_OUTGOING);
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
+ msg.obj = state;
+ mA2dpProfileState.sendMessage(msg);
return true;
}
return false;
}
- private BluetoothProfileConnectionState addProfileState(String address) {
- BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
+ private BluetoothDeviceProfileState addProfileState(String address) {
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) return state;
- state = new BluetoothProfileConnectionState(mContext, address, this, mA2dpService);
- mProfileConnectionMgr.put(address, state);
+ state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
+ mDeviceProfileState.put(address, state);
state.start();
return state;
}
private void removeProfileState(String address) {
- mProfileConnectionMgr.remove(address);
+ mDeviceProfileState.remove(address);
}
private void initProfileState() {
@@ -2003,20 +2023,20 @@ public class BluetoothService extends IBluetooth.Stub {
for (String path : bonds) {
String address = getAddressFromObjectPath(path);
- BluetoothProfileConnectionState state = addProfileState(address);
+ BluetoothDeviceProfileState state = addProfileState(address);
// Allow 8 secs for SDP records to get registered.
Message msg = new Message();
- msg.what = BluetoothProfileConnectionState.AUTO_CONNECT_PROFILES;
+ msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
state.sendMessageDelayed(msg, 8000);
}
}
public boolean notifyIncomingConnection(String address) {
- BluetoothProfileConnectionState state =
- mProfileConnectionMgr.get(address);
+ BluetoothDeviceProfileState state =
+ mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();
- msg.what = BluetoothProfileConnectionState.CONNECT_HFP_INCOMING;
+ msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
state.sendMessage(msg);
return true;
}
@@ -2024,11 +2044,11 @@ public class BluetoothService extends IBluetooth.Stub {
}
/*package*/ boolean notifyIncomingA2dpConnection(String address) {
- BluetoothProfileConnectionState state =
- mProfileConnectionMgr.get(address);
+ BluetoothDeviceProfileState state =
+ mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();
- msg.what = BluetoothProfileConnectionState.CONNECT_A2DP_INCOMING;
+ msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
state.sendMessage(msg);
return true;
}