diff options
-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.java | 144 | ||||
-rw-r--r-- | core/java/android/server/BluetoothService.java | 76 |
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; } |