Loading src/com/android/bluetooth/hfpclient/HeadsetClientService.java +33 −13 Original line number Diff line number Diff line Loading @@ -646,6 +646,24 @@ public class HeadsetClientService extends ProfileService { boolean acceptCall(BluetoothDevice device, int flag) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); /* Phonecalls from a single device are supported, hang up any calls on the other phone */ synchronized (this) { for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap.entrySet()) { if (entry.getValue() == null) { continue; } int connectionState = entry.getValue().getConnectionState(entry.getKey()); if (DBG) { Log.d(TAG, "Accepting a call on device " + device + ". Possibly disconnecting on " + entry.getValue()); } if (connectionState == BluetoothProfile.STATE_CONNECTED) entry.getValue() .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL) .sendToTarget(); } } HeadsetClientStateMachine sm = getStateMachine(device); if (sm == null) { Log.e(TAG, "Cannot allocate SM for device " + device); Loading @@ -653,8 +671,7 @@ public class HeadsetClientService extends ProfileService { } int connectionState = sm.getConnectionState(device); if (connectionState != BluetoothProfile.STATE_CONNECTED && connectionState != BluetoothProfile.STATE_CONNECTING) { if (connectionState != BluetoothProfile.STATE_CONNECTED) { return false; } Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL); Loading Loading @@ -872,20 +889,23 @@ public class HeadsetClientService extends ProfileService { return sm; } // Check if any of the state machines are currently holding the SCO audio stream // This function is *only* called from the SMs which are themselves run the same thread and // hence we do not need synchronization here boolean isScoAvailable() { for (BluetoothDevice bd : mStateMachineMap.keySet()) { HeadsetClientStateMachine sm = mStateMachineMap.get(bd); int audioState = sm.getAudioState(bd); if (audioState != BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED) { Log.w(TAG, "Device " + bd + " audio state " + audioState + " not disconnected"); return false; } // Check if any of the state machines have routed the SCO audio stream. synchronized boolean isScoRouted() { for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap.entrySet()) { if (entry.getValue() != null) { int audioState = entry.getValue().getAudioState(entry.getKey()); if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { if (DBG) { Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState + " Connected"); } return true; } } } return false; } @Override public synchronized void dump(StringBuilder sb) { Loading src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java +23 −6 Original line number Diff line number Diff line Loading @@ -104,6 +104,8 @@ public class HeadsetClientStateMachine extends StateMachine { // Timeouts. static final int CONNECTING_TIMEOUT_MS = 10000; // 10s static final int ROUTING_DELAY_MS = 250; static final int SCO_DISCONNECT_TIMEOUT_MS = 750; static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec. static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec. Loading Loading @@ -460,6 +462,8 @@ public class HeadsetClientStateMachine extends StateMachine { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; } else if (getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) != null) { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3; } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; } else { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; } Loading Loading @@ -581,6 +585,7 @@ public class HeadsetClientStateMachine extends StateMachine { if (c != null) { if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) { addQueuedAction(TERMINATE_CALL, action); sendMessageDelayed(DISCONNECT_AUDIO, SCO_DISCONNECT_TIMEOUT_MS); } else { Log.e(TAG, "ERROR: Couldn't terminate outgoing call"); } Loading Loading @@ -1065,7 +1070,6 @@ public class HeadsetClientStateMachine extends StateMachine { } NativeInterface.connectNative(getByteAddress(device)); // deferMessage(message); break; case DISCONNECT: BluetoothDevice dev = (BluetoothDevice) message.obj; Loading @@ -1083,11 +1087,8 @@ public class HeadsetClientStateMachine extends StateMachine { break; case CONNECT_AUDIO: if (!mService.isScoAvailable() || !NativeInterface.connectAudioNative( getByteAddress(mCurrentDevice))) { Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice + " isScoAvailable " + mService.isScoAvailable()); if (!NativeInterface.connectAudioNative(getByteAddress(mCurrentDevice))) { Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice); broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); Loading Loading @@ -1398,6 +1399,16 @@ public class HeadsetClientStateMachine extends StateMachine { // for routing and volume purposes. // NOTE: All calls here are routed via the setParameters which changes the // routing at the Audio HAL level. if (mService.isScoRouted()) { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = state; event.device = device; sendMessageDelayed(StackEvent.STACK_EVENT, event, ROUTING_DELAY_MS); break; } mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED; // We need to set the volume after switching into HFP mode as some Audio HALs Loading Loading @@ -1501,6 +1512,11 @@ public class HeadsetClientStateMachine extends StateMachine { mAudioManager.setParameters("hfp_enable=false"); } break; case HOLD_CALL: holdCall(); break; case StackEvent.STACK_EVENT: StackEvent event = (StackEvent) message.obj; if (DBG) { Loading Loading @@ -1561,6 +1577,7 @@ public class HeadsetClientStateMachine extends StateMachine { switch (state) { case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: removeMessages(DISCONNECT_AUDIO); mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; // Audio focus may still be held by the entity controlling the actual call // (such as Telecom) and hence this will still keep the call around, there Loading src/com/android/bluetooth/hfpclient/connserv/HfpClientConnection.java +2 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public class HfpClientConnection extends Connection { return; } mHeadsetProfile.connectAudio(device); setInitializing(); setDialing(); finishInitializing(); Loading Loading @@ -256,6 +257,7 @@ public class HfpClientConnection extends Connection { if (!mClosed) { mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_NONE); } mHeadsetProfile.connectAudio(mDevice); } @Override Loading Loading
src/com/android/bluetooth/hfpclient/HeadsetClientService.java +33 −13 Original line number Diff line number Diff line Loading @@ -646,6 +646,24 @@ public class HeadsetClientService extends ProfileService { boolean acceptCall(BluetoothDevice device, int flag) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); /* Phonecalls from a single device are supported, hang up any calls on the other phone */ synchronized (this) { for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap.entrySet()) { if (entry.getValue() == null) { continue; } int connectionState = entry.getValue().getConnectionState(entry.getKey()); if (DBG) { Log.d(TAG, "Accepting a call on device " + device + ". Possibly disconnecting on " + entry.getValue()); } if (connectionState == BluetoothProfile.STATE_CONNECTED) entry.getValue() .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL) .sendToTarget(); } } HeadsetClientStateMachine sm = getStateMachine(device); if (sm == null) { Log.e(TAG, "Cannot allocate SM for device " + device); Loading @@ -653,8 +671,7 @@ public class HeadsetClientService extends ProfileService { } int connectionState = sm.getConnectionState(device); if (connectionState != BluetoothProfile.STATE_CONNECTED && connectionState != BluetoothProfile.STATE_CONNECTING) { if (connectionState != BluetoothProfile.STATE_CONNECTED) { return false; } Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL); Loading Loading @@ -872,20 +889,23 @@ public class HeadsetClientService extends ProfileService { return sm; } // Check if any of the state machines are currently holding the SCO audio stream // This function is *only* called from the SMs which are themselves run the same thread and // hence we do not need synchronization here boolean isScoAvailable() { for (BluetoothDevice bd : mStateMachineMap.keySet()) { HeadsetClientStateMachine sm = mStateMachineMap.get(bd); int audioState = sm.getAudioState(bd); if (audioState != BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED) { Log.w(TAG, "Device " + bd + " audio state " + audioState + " not disconnected"); return false; } // Check if any of the state machines have routed the SCO audio stream. synchronized boolean isScoRouted() { for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap.entrySet()) { if (entry.getValue() != null) { int audioState = entry.getValue().getAudioState(entry.getKey()); if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { if (DBG) { Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState + " Connected"); } return true; } } } return false; } @Override public synchronized void dump(StringBuilder sb) { Loading
src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java +23 −6 Original line number Diff line number Diff line Loading @@ -104,6 +104,8 @@ public class HeadsetClientStateMachine extends StateMachine { // Timeouts. static final int CONNECTING_TIMEOUT_MS = 10000; // 10s static final int ROUTING_DELAY_MS = 250; static final int SCO_DISCONNECT_TIMEOUT_MS = 750; static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec. static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec. Loading Loading @@ -460,6 +462,8 @@ public class HeadsetClientStateMachine extends StateMachine { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; } else if (getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) != null) { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3; } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; } else { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; } Loading Loading @@ -581,6 +585,7 @@ public class HeadsetClientStateMachine extends StateMachine { if (c != null) { if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) { addQueuedAction(TERMINATE_CALL, action); sendMessageDelayed(DISCONNECT_AUDIO, SCO_DISCONNECT_TIMEOUT_MS); } else { Log.e(TAG, "ERROR: Couldn't terminate outgoing call"); } Loading Loading @@ -1065,7 +1070,6 @@ public class HeadsetClientStateMachine extends StateMachine { } NativeInterface.connectNative(getByteAddress(device)); // deferMessage(message); break; case DISCONNECT: BluetoothDevice dev = (BluetoothDevice) message.obj; Loading @@ -1083,11 +1087,8 @@ public class HeadsetClientStateMachine extends StateMachine { break; case CONNECT_AUDIO: if (!mService.isScoAvailable() || !NativeInterface.connectAudioNative( getByteAddress(mCurrentDevice))) { Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice + " isScoAvailable " + mService.isScoAvailable()); if (!NativeInterface.connectAudioNative(getByteAddress(mCurrentDevice))) { Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice); broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); Loading Loading @@ -1398,6 +1399,16 @@ public class HeadsetClientStateMachine extends StateMachine { // for routing and volume purposes. // NOTE: All calls here are routed via the setParameters which changes the // routing at the Audio HAL level. if (mService.isScoRouted()) { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = state; event.device = device; sendMessageDelayed(StackEvent.STACK_EVENT, event, ROUTING_DELAY_MS); break; } mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED; // We need to set the volume after switching into HFP mode as some Audio HALs Loading Loading @@ -1501,6 +1512,11 @@ public class HeadsetClientStateMachine extends StateMachine { mAudioManager.setParameters("hfp_enable=false"); } break; case HOLD_CALL: holdCall(); break; case StackEvent.STACK_EVENT: StackEvent event = (StackEvent) message.obj; if (DBG) { Loading Loading @@ -1561,6 +1577,7 @@ public class HeadsetClientStateMachine extends StateMachine { switch (state) { case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: removeMessages(DISCONNECT_AUDIO); mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; // Audio focus may still be held by the entity controlling the actual call // (such as Telecom) and hence this will still keep the call around, there Loading
src/com/android/bluetooth/hfpclient/connserv/HfpClientConnection.java +2 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public class HfpClientConnection extends Connection { return; } mHeadsetProfile.connectAudio(device); setInitializing(); setDialing(); finishInitializing(); Loading Loading @@ -256,6 +257,7 @@ public class HfpClientConnection extends Connection { if (!mClosed) { mHeadsetProfile.acceptCall(mDevice, BluetoothHeadsetClient.CALL_ACCEPT_NONE); } mHeadsetProfile.connectAudio(mDevice); } @Override Loading