Merge "Serialize all commands for a particular profile." into kraken

This commit is contained in:
Jaikumar Ganesh
2010-06-07 14:10:08 -07:00
committed by Android (Google) Code Review
3 changed files with 233 additions and 45 deletions

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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;
}
return false;
}
public boolean disconnectSink(String address) {
BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
if (state != null) {
state.sendMessage(BluetoothProfileConnectionState.DISCONNECT_A2DP_OUTGOING);
Message msg = new Message();
msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
msg.obj = state;
mA2dpProfileState.sendMessage(msg);
return true;
}
return false;
}
private BluetoothProfileConnectionState addProfileState(String address) {
BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
public boolean disconnectSink(String address) {
BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();
msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
msg.obj = state;
mA2dpProfileState.sendMessage(msg);
return true;
}
return false;
}
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;
}