Loading core/java/android/bluetooth/BluetoothProfileConnectionState.java→core/java/android/bluetooth/BluetoothDeviceProfileState.java +35 −11 Original line number Original line Diff line number Diff line /* /* * 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"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading Loading @@ -54,8 +54,8 @@ import com.android.internal.util.HierarchicalStateMachine; * Todo(): Write tests for this class, when the Android Mock support is completed. * Todo(): Write tests for this class, when the Android Mock support is completed. * @hide * @hide */ */ public final class BluetoothProfileConnectionState extends HierarchicalStateMachine { public final class BluetoothDeviceProfileState extends HierarchicalStateMachine { private static final String TAG = "BluetoothProfileConnectionState"; private static final String TAG = "BluetoothDeviceProfileState"; private static final boolean DBG = true; //STOPSHIP - Change to false private static final boolean DBG = true; //STOPSHIP - Change to false public static final int CONNECT_HFP_OUTGOING = 1; public static final int CONNECT_HFP_OUTGOING = 1; Loading @@ -72,7 +72,7 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach public static final int AUTO_CONNECT_PROFILES = 10; public static final int AUTO_CONNECT_PROFILES = 10; public static final int TRANSITION_TO_STABLE = 11; 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 BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); Loading Loading @@ -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) { BluetoothService service, BluetoothA2dpService a2dpService) { super(address); super(address); mContext = context; mContext = context; Loading Loading @@ -168,12 +183,12 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach mHeadsetService = new BluetoothHeadset(mContext, this); mHeadsetService = new BluetoothHeadset(mContext, this); } } public void onServiceConnected() { public void onServiceConnected() { synchronized(BluetoothProfileConnectionState.this) { synchronized(BluetoothDeviceProfileState.this) { mHeadsetServiceConnected = true; mHeadsetServiceConnected = true; } } } } public void onServiceDisconnected() { public void onServiceDisconnected() { synchronized(BluetoothProfileConnectionState.this) { synchronized(BluetoothDeviceProfileState.this) { mHeadsetServiceConnected = false; mHeadsetServiceConnected = false; } } } } Loading Loading @@ -222,16 +237,21 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach processCommand(UNPAIR); processCommand(UNPAIR); break; break; case AUTO_CONNECT_PROFILES: case AUTO_CONNECT_PROFILES: if (!mHeadsetServiceConnected) { if (isPhoneDocked(mDevice)) { // Don't auto connect to docks. break; } else if (!mHeadsetServiceConnected) { deferMessage(message); deferMessage(message); } else { } else { if (mHeadsetService.getPriority(mDevice) == if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT) { BluetoothHeadset.PRIORITY_AUTO_CONNECT && !mHeadsetService.isConnected(mDevice)) { mHeadsetService.connectHeadset(mDevice); mHeadsetService.connectHeadset(mDevice); } } if (mA2dpService != null && if (mA2dpService != null && mA2dpService.getSinkPriority(mDevice) == mA2dpService.getSinkPriority(mDevice) == BluetoothA2dp.PRIORITY_AUTO_CONNECT) { BluetoothA2dp.PRIORITY_AUTO_CONNECT && mA2dpService.getConnectedSinks().length == 0) { mA2dpService.connectSink(mDevice); mA2dpService.connectSink(mDevice); } } } } Loading Loading @@ -633,6 +653,10 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach return false; return false; } } /*package*/ BluetoothDevice getDevice() { return mDevice; } private void log(String message) { private void log(String message) { if (DBG) { if (DBG) { Log.i(TAG, "Device:" + mDevice + " Message:" + message); Log.i(TAG, "Device:" + mDevice + " Message:" + message); Loading core/java/android/bluetooth/BluetoothProfileState.java 0 → 100644 +144 −0 Original line number Original line Diff line number Diff line /* * 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); } } } core/java/android/server/BluetoothService.java +48 −28 Original line number Original line Diff line number Diff line Loading @@ -28,7 +28,8 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfileConnectionState; import android.bluetooth.BluetoothDeviceProfileState; import android.bluetooth.BluetoothProfileState; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetooth; Loading Loading @@ -123,7 +124,9 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap<Integer, Integer> mServiceRecordToPid; 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 BluetoothA2dpService mA2dpService; private static String mDockAddress; private static String mDockAddress; Loading Loading @@ -183,7 +186,12 @@ public class BluetoothService extends IBluetooth.Stub { mUuidIntentTracker = new ArrayList<String>(); mUuidIntentTracker = new ArrayList<String>(); mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); mServiceRecordToPid = new HashMap<Integer, Integer>(); 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(); IntentFilter filter = new IntentFilter(); registerForAirplaneMode(filter); registerForAirplaneMode(filter); Loading Loading @@ -1179,9 +1187,9 @@ public class BluetoothService extends IBluetooth.Stub { if (!BluetoothAdapter.checkBluetoothAddress(address)) { if (!BluetoothAdapter.checkBluetoothAddress(address)) { return false; return false; } } BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { if (state != null) { state.sendMessage(BluetoothProfileConnectionState.UNPAIR); state.sendMessage(BluetoothDeviceProfileState.UNPAIR); return true; return true; } else { } else { return false; return false; Loading Loading @@ -1942,53 +1950,65 @@ public class BluetoothService extends IBluetooth.Stub { } } public boolean connectHeadset(String address) { public boolean connectHeadset(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { if (state != null) { state.sendMessage(BluetoothProfileConnectionState.CONNECT_HFP_OUTGOING); Message msg = new Message(); msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING; msg.obj = state; mHfpProfileState.sendMessage(msg); return true; return true; } } return false; return false; } } public boolean disconnectHeadset(String address) { public boolean disconnectHeadset(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { 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 true; } } return false; return false; } } public boolean connectSink(String address) { public boolean connectSink(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { if (state != null) { state.sendMessage(BluetoothProfileConnectionState.CONNECT_A2DP_OUTGOING); Message msg = new Message(); msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING; msg.obj = state; mA2dpProfileState.sendMessage(msg); return true; return true; } } return false; return false; } } public boolean disconnectSink(String address) { public boolean disconnectSink(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { 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 true; } } return false; return false; } } private BluetoothProfileConnectionState addProfileState(String address) { private BluetoothDeviceProfileState addProfileState(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) return state; if (state != null) return state; state = new BluetoothProfileConnectionState(mContext, address, this, mA2dpService); state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService); mProfileConnectionMgr.put(address, state); mDeviceProfileState.put(address, state); state.start(); state.start(); return state; return state; } } private void removeProfileState(String address) { private void removeProfileState(String address) { mProfileConnectionMgr.remove(address); mDeviceProfileState.remove(address); } } private void initProfileState() { private void initProfileState() { Loading @@ -2003,20 +2023,20 @@ public class BluetoothService extends IBluetooth.Stub { for (String path : bonds) { for (String path : bonds) { String address = getAddressFromObjectPath(path); String address = getAddressFromObjectPath(path); BluetoothProfileConnectionState state = addProfileState(address); BluetoothDeviceProfileState state = addProfileState(address); // Allow 8 secs for SDP records to get registered. // Allow 8 secs for SDP records to get registered. Message msg = new Message(); Message msg = new Message(); msg.what = BluetoothProfileConnectionState.AUTO_CONNECT_PROFILES; msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES; state.sendMessageDelayed(msg, 8000); state.sendMessageDelayed(msg, 8000); } } } } public boolean notifyIncomingConnection(String address) { public boolean notifyIncomingConnection(String address) { BluetoothProfileConnectionState state = BluetoothDeviceProfileState state = mProfileConnectionMgr.get(address); mDeviceProfileState.get(address); if (state != null) { if (state != null) { Message msg = new Message(); Message msg = new Message(); msg.what = BluetoothProfileConnectionState.CONNECT_HFP_INCOMING; msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING; state.sendMessage(msg); state.sendMessage(msg); return true; return true; } } Loading @@ -2024,11 +2044,11 @@ public class BluetoothService extends IBluetooth.Stub { } } /*package*/ boolean notifyIncomingA2dpConnection(String address) { /*package*/ boolean notifyIncomingA2dpConnection(String address) { BluetoothProfileConnectionState state = BluetoothDeviceProfileState state = mProfileConnectionMgr.get(address); mDeviceProfileState.get(address); if (state != null) { if (state != null) { Message msg = new Message(); Message msg = new Message(); msg.what = BluetoothProfileConnectionState.CONNECT_A2DP_INCOMING; msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING; state.sendMessage(msg); state.sendMessage(msg); return true; return true; } } Loading Loading
core/java/android/bluetooth/BluetoothProfileConnectionState.java→core/java/android/bluetooth/BluetoothDeviceProfileState.java +35 −11 Original line number Original line Diff line number Diff line /* /* * 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"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading Loading @@ -54,8 +54,8 @@ import com.android.internal.util.HierarchicalStateMachine; * Todo(): Write tests for this class, when the Android Mock support is completed. * Todo(): Write tests for this class, when the Android Mock support is completed. * @hide * @hide */ */ public final class BluetoothProfileConnectionState extends HierarchicalStateMachine { public final class BluetoothDeviceProfileState extends HierarchicalStateMachine { private static final String TAG = "BluetoothProfileConnectionState"; private static final String TAG = "BluetoothDeviceProfileState"; private static final boolean DBG = true; //STOPSHIP - Change to false private static final boolean DBG = true; //STOPSHIP - Change to false public static final int CONNECT_HFP_OUTGOING = 1; public static final int CONNECT_HFP_OUTGOING = 1; Loading @@ -72,7 +72,7 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach public static final int AUTO_CONNECT_PROFILES = 10; public static final int AUTO_CONNECT_PROFILES = 10; public static final int TRANSITION_TO_STABLE = 11; 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 BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); Loading Loading @@ -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) { BluetoothService service, BluetoothA2dpService a2dpService) { super(address); super(address); mContext = context; mContext = context; Loading Loading @@ -168,12 +183,12 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach mHeadsetService = new BluetoothHeadset(mContext, this); mHeadsetService = new BluetoothHeadset(mContext, this); } } public void onServiceConnected() { public void onServiceConnected() { synchronized(BluetoothProfileConnectionState.this) { synchronized(BluetoothDeviceProfileState.this) { mHeadsetServiceConnected = true; mHeadsetServiceConnected = true; } } } } public void onServiceDisconnected() { public void onServiceDisconnected() { synchronized(BluetoothProfileConnectionState.this) { synchronized(BluetoothDeviceProfileState.this) { mHeadsetServiceConnected = false; mHeadsetServiceConnected = false; } } } } Loading Loading @@ -222,16 +237,21 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach processCommand(UNPAIR); processCommand(UNPAIR); break; break; case AUTO_CONNECT_PROFILES: case AUTO_CONNECT_PROFILES: if (!mHeadsetServiceConnected) { if (isPhoneDocked(mDevice)) { // Don't auto connect to docks. break; } else if (!mHeadsetServiceConnected) { deferMessage(message); deferMessage(message); } else { } else { if (mHeadsetService.getPriority(mDevice) == if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT) { BluetoothHeadset.PRIORITY_AUTO_CONNECT && !mHeadsetService.isConnected(mDevice)) { mHeadsetService.connectHeadset(mDevice); mHeadsetService.connectHeadset(mDevice); } } if (mA2dpService != null && if (mA2dpService != null && mA2dpService.getSinkPriority(mDevice) == mA2dpService.getSinkPriority(mDevice) == BluetoothA2dp.PRIORITY_AUTO_CONNECT) { BluetoothA2dp.PRIORITY_AUTO_CONNECT && mA2dpService.getConnectedSinks().length == 0) { mA2dpService.connectSink(mDevice); mA2dpService.connectSink(mDevice); } } } } Loading Loading @@ -633,6 +653,10 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach return false; return false; } } /*package*/ BluetoothDevice getDevice() { return mDevice; } private void log(String message) { private void log(String message) { if (DBG) { if (DBG) { Log.i(TAG, "Device:" + mDevice + " Message:" + message); Log.i(TAG, "Device:" + mDevice + " Message:" + message); Loading
core/java/android/bluetooth/BluetoothProfileState.java 0 → 100644 +144 −0 Original line number Original line Diff line number Diff line /* * 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); } } }
core/java/android/server/BluetoothService.java +48 −28 Original line number Original line Diff line number Diff line Loading @@ -28,7 +28,8 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfileConnectionState; import android.bluetooth.BluetoothDeviceProfileState; import android.bluetooth.BluetoothProfileState; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetooth; Loading Loading @@ -123,7 +124,9 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap<Integer, Integer> mServiceRecordToPid; 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 BluetoothA2dpService mA2dpService; private static String mDockAddress; private static String mDockAddress; Loading Loading @@ -183,7 +186,12 @@ public class BluetoothService extends IBluetooth.Stub { mUuidIntentTracker = new ArrayList<String>(); mUuidIntentTracker = new ArrayList<String>(); mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); mServiceRecordToPid = new HashMap<Integer, Integer>(); 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(); IntentFilter filter = new IntentFilter(); registerForAirplaneMode(filter); registerForAirplaneMode(filter); Loading Loading @@ -1179,9 +1187,9 @@ public class BluetoothService extends IBluetooth.Stub { if (!BluetoothAdapter.checkBluetoothAddress(address)) { if (!BluetoothAdapter.checkBluetoothAddress(address)) { return false; return false; } } BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { if (state != null) { state.sendMessage(BluetoothProfileConnectionState.UNPAIR); state.sendMessage(BluetoothDeviceProfileState.UNPAIR); return true; return true; } else { } else { return false; return false; Loading Loading @@ -1942,53 +1950,65 @@ public class BluetoothService extends IBluetooth.Stub { } } public boolean connectHeadset(String address) { public boolean connectHeadset(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { if (state != null) { state.sendMessage(BluetoothProfileConnectionState.CONNECT_HFP_OUTGOING); Message msg = new Message(); msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING; msg.obj = state; mHfpProfileState.sendMessage(msg); return true; return true; } } return false; return false; } } public boolean disconnectHeadset(String address) { public boolean disconnectHeadset(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { 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 true; } } return false; return false; } } public boolean connectSink(String address) { public boolean connectSink(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { if (state != null) { state.sendMessage(BluetoothProfileConnectionState.CONNECT_A2DP_OUTGOING); Message msg = new Message(); msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING; msg.obj = state; mA2dpProfileState.sendMessage(msg); return true; return true; } } return false; return false; } } public boolean disconnectSink(String address) { public boolean disconnectSink(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { 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 true; } } return false; return false; } } private BluetoothProfileConnectionState addProfileState(String address) { private BluetoothDeviceProfileState addProfileState(String address) { BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address); BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) return state; if (state != null) return state; state = new BluetoothProfileConnectionState(mContext, address, this, mA2dpService); state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService); mProfileConnectionMgr.put(address, state); mDeviceProfileState.put(address, state); state.start(); state.start(); return state; return state; } } private void removeProfileState(String address) { private void removeProfileState(String address) { mProfileConnectionMgr.remove(address); mDeviceProfileState.remove(address); } } private void initProfileState() { private void initProfileState() { Loading @@ -2003,20 +2023,20 @@ public class BluetoothService extends IBluetooth.Stub { for (String path : bonds) { for (String path : bonds) { String address = getAddressFromObjectPath(path); String address = getAddressFromObjectPath(path); BluetoothProfileConnectionState state = addProfileState(address); BluetoothDeviceProfileState state = addProfileState(address); // Allow 8 secs for SDP records to get registered. // Allow 8 secs for SDP records to get registered. Message msg = new Message(); Message msg = new Message(); msg.what = BluetoothProfileConnectionState.AUTO_CONNECT_PROFILES; msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES; state.sendMessageDelayed(msg, 8000); state.sendMessageDelayed(msg, 8000); } } } } public boolean notifyIncomingConnection(String address) { public boolean notifyIncomingConnection(String address) { BluetoothProfileConnectionState state = BluetoothDeviceProfileState state = mProfileConnectionMgr.get(address); mDeviceProfileState.get(address); if (state != null) { if (state != null) { Message msg = new Message(); Message msg = new Message(); msg.what = BluetoothProfileConnectionState.CONNECT_HFP_INCOMING; msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING; state.sendMessage(msg); state.sendMessage(msg); return true; return true; } } Loading @@ -2024,11 +2044,11 @@ public class BluetoothService extends IBluetooth.Stub { } } /*package*/ boolean notifyIncomingA2dpConnection(String address) { /*package*/ boolean notifyIncomingA2dpConnection(String address) { BluetoothProfileConnectionState state = BluetoothDeviceProfileState state = mProfileConnectionMgr.get(address); mDeviceProfileState.get(address); if (state != null) { if (state != null) { Message msg = new Message(); Message msg = new Message(); msg.what = BluetoothProfileConnectionState.CONNECT_A2DP_INCOMING; msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING; state.sendMessage(msg); state.sendMessage(msg); return true; return true; } } Loading