Loading src/com/android/server/telecom/CallAudioManager.java +34 −4 Original line number Diff line number Diff line Loading @@ -36,6 +36,8 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.LinkedHashSet; import java.util.stream.Collectors; public class CallAudioManager extends CallsManagerListenerBase { Loading Loading @@ -116,7 +118,7 @@ public class CallAudioManager extends CallsManagerListenerBase { // State did not change, so no need to do anything. return; } Log.d(LOG_TAG, "Call state changed for TC@%s: %s -> %s", call.getId(), Log.i(this, "onCallStateChanged: Call state changed for TC@%s: %s -> %s", call.getId(), CallState.toString(oldState), CallState.toString(newState)); removeCallFromAllBins(call); Loading Loading @@ -761,6 +763,7 @@ public class CallAudioManager extends CallsManagerListenerBase { private void updateForegroundCall() { Call oldForegroundCall = mForegroundCall; if (mActiveDialingOrConnectingCalls.size() > 0) { // Give preference for connecting calls over active/dialing for foreground-ness. Call possibleConnectingCall = null; Loading @@ -769,8 +772,21 @@ public class CallAudioManager extends CallsManagerListenerBase { possibleConnectingCall = call; } } mForegroundCall = possibleConnectingCall == null ? mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall; // Prefer a connecting call if (possibleConnectingCall != null) { mForegroundCall = possibleConnectingCall; } else { // Next, prefer an active or dialing call which is not in the process of being // disconnected. mForegroundCall = mActiveDialingOrConnectingCalls .stream() .filter(c -> (c.getState() == CallState.ACTIVE || c.getState() == CallState.DIALING) && !c.isLocallyDisconnecting()) .findFirst() // If we can't find one, then just fall back to the first one. .orElse(mActiveDialingOrConnectingCalls.iterator().next()); } } else if (mRingingCalls.size() > 0) { mForegroundCall = mRingingCalls.iterator().next(); } else if (mHoldingCalls.size() > 0) { Loading @@ -778,10 +794,24 @@ public class CallAudioManager extends CallsManagerListenerBase { } else { mForegroundCall = null; } Log.i(this, "updateForegroundCall; oldFg=%s, newFg=%s, aDC=%s, ring=%s, hold=%s", (oldForegroundCall == null ? "none" : oldForegroundCall.getId()), (mForegroundCall == null ? "none" : mForegroundCall.getId()), mActiveDialingOrConnectingCalls.stream().map(c -> c.getId()).collect( Collectors.joining(",")), mRingingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")), mHoldingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")) ); if (mForegroundCall != oldForegroundCall) { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); if (mForegroundCall != null) { // Ensure the voip audio mode for the new foreground call is taken into account. mCallAudioModeStateMachine.sendMessageWithArgs( CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, makeArgsForModeStateMachine()); } mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall); maybePlayHoldTone(); } Loading tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java +73 −3 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; Loading Loading @@ -277,7 +278,8 @@ public class CallAudioManagerTest extends TelecomTestCase { verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); assertMessageArgEquality(expectedArgs, captor.getValue()); verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( // Expet another invocation due to audio mode change signal. verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); Loading @@ -286,7 +288,7 @@ public class CallAudioManagerTest extends TelecomTestCase { verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); assertMessageArgEquality(expectedArgs, captor.getValue()); verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( verify(mCallAudioModeStateMachine, times(4)).sendMessageWithArgs( anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); disconnectCall(call); Loading Loading @@ -327,7 +329,8 @@ public class CallAudioManagerTest extends TelecomTestCase { verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); assertMessageArgEquality(expectedArgs, captor.getValue()); verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( // Expect an extra time due to audio mode change signal verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); // Ensure we started ringback. Loading Loading @@ -702,6 +705,73 @@ public class CallAudioManagerTest extends TelecomTestCase { assertFalse(captor.getValue().isStreaming); } @SmallTest @Test public void testTriggerAudioManagerModeChange() { // Start with an incoming PSTN call Call pstnCall = mock(Call.class); when(pstnCall.getState()).thenReturn(CallState.RINGING); when(pstnCall.getIsVoipAudioMode()).thenReturn(false); ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor(); // Add the call mCallAudioManager.onCallAdded(pstnCall); verify(mCallAudioModeStateMachine).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), captor.capture()); CallAudioModeStateMachine.MessageArgs expectedArgs = new Builder() .setHasActiveOrDialingCalls(false) .setHasRingingCalls(true) .setHasHoldingCalls(false) .setIsTonePlaying(false) .setHasAudioProcessingCalls(false) .setForegroundCallIsVoip(false) .setSession(null) .setForegroundCallIsVoip(false) .build(); assertMessageArgEquality(expectedArgs, captor.getValue()); clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls // Make call active; don't expect there to be an audio mode transition. when(pstnCall.getState()).thenReturn(CallState.ACTIVE); mCallAudioManager.onCallStateChanged(pstnCall, CallState.RINGING, CallState.ACTIVE); verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), any(CallAudioModeStateMachine.MessageArgs.class)); clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls // Add a new Voip call in ringing state; this should not result in a direct audio mode // change. Call voipCall = mock(Call.class); when(voipCall.getState()).thenReturn(CallState.RINGING); when(voipCall.getIsVoipAudioMode()).thenReturn(true); mCallAudioManager.onCallAdded(voipCall); verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), any(CallAudioModeStateMachine.MessageArgs.class)); clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls // Make voip call active and set the PSTN call to locally disconnecting; the new foreground // call will be the voip call. when(pstnCall.isLocallyDisconnecting()).thenReturn(true); when(voipCall.getState()).thenReturn(CallState.ACTIVE); mCallAudioManager.onCallStateChanged(voipCall, CallState.RINGING, CallState.ACTIVE); verify(mCallAudioModeStateMachine).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), captor.capture()); CallAudioModeStateMachine.MessageArgs expectedArgs2 = new Builder() .setHasActiveOrDialingCalls(true) .setHasRingingCalls(false) .setHasHoldingCalls(false) .setIsTonePlaying(false) .setHasAudioProcessingCalls(false) .setForegroundCallIsVoip(false) .setSession(null) .setForegroundCallIsVoip(true) .build(); assertMessageArgEquality(expectedArgs2, captor.getValue()); } private Call createSimulatedRingingCall() { Call call = mock(Call.class); when(call.getState()).thenReturn(CallState.SIMULATED_RINGING); Loading Loading
src/com/android/server/telecom/CallAudioManager.java +34 −4 Original line number Diff line number Diff line Loading @@ -36,6 +36,8 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.LinkedHashSet; import java.util.stream.Collectors; public class CallAudioManager extends CallsManagerListenerBase { Loading Loading @@ -116,7 +118,7 @@ public class CallAudioManager extends CallsManagerListenerBase { // State did not change, so no need to do anything. return; } Log.d(LOG_TAG, "Call state changed for TC@%s: %s -> %s", call.getId(), Log.i(this, "onCallStateChanged: Call state changed for TC@%s: %s -> %s", call.getId(), CallState.toString(oldState), CallState.toString(newState)); removeCallFromAllBins(call); Loading Loading @@ -761,6 +763,7 @@ public class CallAudioManager extends CallsManagerListenerBase { private void updateForegroundCall() { Call oldForegroundCall = mForegroundCall; if (mActiveDialingOrConnectingCalls.size() > 0) { // Give preference for connecting calls over active/dialing for foreground-ness. Call possibleConnectingCall = null; Loading @@ -769,8 +772,21 @@ public class CallAudioManager extends CallsManagerListenerBase { possibleConnectingCall = call; } } mForegroundCall = possibleConnectingCall == null ? mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall; // Prefer a connecting call if (possibleConnectingCall != null) { mForegroundCall = possibleConnectingCall; } else { // Next, prefer an active or dialing call which is not in the process of being // disconnected. mForegroundCall = mActiveDialingOrConnectingCalls .stream() .filter(c -> (c.getState() == CallState.ACTIVE || c.getState() == CallState.DIALING) && !c.isLocallyDisconnecting()) .findFirst() // If we can't find one, then just fall back to the first one. .orElse(mActiveDialingOrConnectingCalls.iterator().next()); } } else if (mRingingCalls.size() > 0) { mForegroundCall = mRingingCalls.iterator().next(); } else if (mHoldingCalls.size() > 0) { Loading @@ -778,10 +794,24 @@ public class CallAudioManager extends CallsManagerListenerBase { } else { mForegroundCall = null; } Log.i(this, "updateForegroundCall; oldFg=%s, newFg=%s, aDC=%s, ring=%s, hold=%s", (oldForegroundCall == null ? "none" : oldForegroundCall.getId()), (mForegroundCall == null ? "none" : mForegroundCall.getId()), mActiveDialingOrConnectingCalls.stream().map(c -> c.getId()).collect( Collectors.joining(",")), mRingingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")), mHoldingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")) ); if (mForegroundCall != oldForegroundCall) { mCallAudioRouteStateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); if (mForegroundCall != null) { // Ensure the voip audio mode for the new foreground call is taken into account. mCallAudioModeStateMachine.sendMessageWithArgs( CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, makeArgsForModeStateMachine()); } mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall); maybePlayHoldTone(); } Loading
tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java +73 −3 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; Loading Loading @@ -277,7 +278,8 @@ public class CallAudioManagerTest extends TelecomTestCase { verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); assertMessageArgEquality(expectedArgs, captor.getValue()); verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( // Expet another invocation due to audio mode change signal. verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); Loading @@ -286,7 +288,7 @@ public class CallAudioManagerTest extends TelecomTestCase { verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); assertMessageArgEquality(expectedArgs, captor.getValue()); verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( verify(mCallAudioModeStateMachine, times(4)).sendMessageWithArgs( anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); disconnectCall(call); Loading Loading @@ -327,7 +329,8 @@ public class CallAudioManagerTest extends TelecomTestCase { verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); assertMessageArgEquality(expectedArgs, captor.getValue()); verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( // Expect an extra time due to audio mode change signal verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); // Ensure we started ringback. Loading Loading @@ -702,6 +705,73 @@ public class CallAudioManagerTest extends TelecomTestCase { assertFalse(captor.getValue().isStreaming); } @SmallTest @Test public void testTriggerAudioManagerModeChange() { // Start with an incoming PSTN call Call pstnCall = mock(Call.class); when(pstnCall.getState()).thenReturn(CallState.RINGING); when(pstnCall.getIsVoipAudioMode()).thenReturn(false); ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor(); // Add the call mCallAudioManager.onCallAdded(pstnCall); verify(mCallAudioModeStateMachine).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), captor.capture()); CallAudioModeStateMachine.MessageArgs expectedArgs = new Builder() .setHasActiveOrDialingCalls(false) .setHasRingingCalls(true) .setHasHoldingCalls(false) .setIsTonePlaying(false) .setHasAudioProcessingCalls(false) .setForegroundCallIsVoip(false) .setSession(null) .setForegroundCallIsVoip(false) .build(); assertMessageArgEquality(expectedArgs, captor.getValue()); clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls // Make call active; don't expect there to be an audio mode transition. when(pstnCall.getState()).thenReturn(CallState.ACTIVE); mCallAudioManager.onCallStateChanged(pstnCall, CallState.RINGING, CallState.ACTIVE); verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), any(CallAudioModeStateMachine.MessageArgs.class)); clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls // Add a new Voip call in ringing state; this should not result in a direct audio mode // change. Call voipCall = mock(Call.class); when(voipCall.getState()).thenReturn(CallState.RINGING); when(voipCall.getIsVoipAudioMode()).thenReturn(true); mCallAudioManager.onCallAdded(voipCall); verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), any(CallAudioModeStateMachine.MessageArgs.class)); clearInvocations(mCallAudioModeStateMachine); // Avoid verifying for previous calls // Make voip call active and set the PSTN call to locally disconnecting; the new foreground // call will be the voip call. when(pstnCall.isLocallyDisconnecting()).thenReturn(true); when(voipCall.getState()).thenReturn(CallState.ACTIVE); mCallAudioManager.onCallStateChanged(voipCall, CallState.RINGING, CallState.ACTIVE); verify(mCallAudioModeStateMachine).sendMessageWithArgs( eq(CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE), captor.capture()); CallAudioModeStateMachine.MessageArgs expectedArgs2 = new Builder() .setHasActiveOrDialingCalls(true) .setHasRingingCalls(false) .setHasHoldingCalls(false) .setIsTonePlaying(false) .setHasAudioProcessingCalls(false) .setForegroundCallIsVoip(false) .setSession(null) .setForegroundCallIsVoip(true) .build(); assertMessageArgEquality(expectedArgs2, captor.getValue()); } private Call createSimulatedRingingCall() { Call call = mock(Call.class); when(call.getState()).thenReturn(CallState.SIMULATED_RINGING); Loading