Merge "Serialize all commands for a particular profile." into kraken
This commit is contained in:
committed by
Android (Google) Code Review
commit
526f2ff1a5
@@ -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);
|
||||
144
core/java/android/bluetooth/BluetoothProfileState.java
Normal file
144
core/java/android/bluetooth/BluetoothProfileState.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user