Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.server.telecom.tests;
     18 
     19 import android.media.ToneGenerator;
     20 import android.telecom.DisconnectCause;
     21 import android.test.suitebuilder.annotation.MediumTest;
     22 import android.util.SparseArray;
     23 
     24 import com.android.server.telecom.Call;
     25 import com.android.server.telecom.CallAudioModeStateMachine;
     26 import com.android.server.telecom.CallAudioRouteStateMachine;
     27 import com.android.server.telecom.CallState;
     28 import com.android.server.telecom.CallsManager;
     29 import com.android.server.telecom.CallAudioManager;
     30 import com.android.server.telecom.DtmfLocalTonePlayer;
     31 import com.android.server.telecom.InCallTonePlayer;
     32 import com.android.server.telecom.RingbackPlayer;
     33 import com.android.server.telecom.Ringer;
     34 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
     35 
     36 import org.junit.Before;
     37 import org.junit.Test;
     38 import org.junit.runner.RunWith;
     39 import org.junit.runners.JUnit4;
     40 import org.mockito.ArgumentCaptor;
     41 import org.mockito.Mock;
     42 
     43 import java.util.LinkedHashSet;
     44 import java.util.List;
     45 import java.util.stream.Collectors;
     46 
     47 import static org.junit.Assert.assertEquals;
     48 import static org.junit.Assert.assertTrue;
     49 import static org.mockito.Matchers.any;
     50 import static org.mockito.Matchers.anyInt;
     51 import static org.mockito.Matchers.eq;
     52 import static org.mockito.Mockito.atLeastOnce;
     53 import static org.mockito.Mockito.doAnswer;
     54 import static org.mockito.Mockito.mock;
     55 import static org.mockito.Mockito.times;
     56 import static org.mockito.Mockito.verify;
     57 import static org.mockito.Mockito.when;
     58 
     59 @RunWith(JUnit4.class)
     60 public class CallAudioManagerTest extends TelecomTestCase {
     61     @Mock private CallAudioRouteStateMachine mCallAudioRouteStateMachine;
     62     @Mock private CallsManager mCallsManager;
     63     @Mock private CallAudioModeStateMachine mCallAudioModeStateMachine;
     64     @Mock private InCallTonePlayer.Factory mPlayerFactory;
     65     @Mock private Ringer mRinger;
     66     @Mock private RingbackPlayer mRingbackPlayer;
     67     @Mock private DtmfLocalTonePlayer mDtmfLocalTonePlayer;
     68     @Mock private BluetoothStateReceiver mBluetoothStateReceiver;
     69 
     70     private CallAudioManager mCallAudioManager;
     71 
     72     @Override
     73     @Before
     74     public void setUp() throws Exception {
     75         super.setUp();
     76         doAnswer((invocation) -> {
     77             InCallTonePlayer mockInCallTonePlayer = mock(InCallTonePlayer.class);
     78             doAnswer((invocation2) -> {
     79                 mCallAudioManager.setIsTonePlaying(true);
     80                 return null;
     81             }).when(mockInCallTonePlayer).startTone();
     82             return mockInCallTonePlayer;
     83         }).when(mPlayerFactory).createPlayer(anyInt());
     84         mCallAudioManager = new CallAudioManager(
     85                 mCallAudioRouteStateMachine,
     86                 mCallsManager,
     87                 mCallAudioModeStateMachine,
     88                 mPlayerFactory,
     89                 mRinger,
     90                 mRingbackPlayer,
     91                 mBluetoothStateReceiver,
     92                 mDtmfLocalTonePlayer);
     93     }
     94 
     95     @MediumTest
     96     @Test
     97     public void testUnmuteOfSecondIncomingCall() {
     98         // Start with a single incoming call.
     99         Call call = createIncomingCall();
    100         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
    101                 .thenReturn(false);
    102         when(call.getId()).thenReturn("1");
    103 
    104         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
    105                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
    106         // Answer the incoming call
    107         mCallAudioManager.onIncomingCallAnswered(call);
    108         when(call.getState()).thenReturn(CallState.ACTIVE);
    109         mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE);
    110         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    111                 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
    112         CallAudioModeStateMachine.MessageArgs correctArgs =
    113                 new CallAudioModeStateMachine.MessageArgs(
    114                         true, // hasActiveOrDialingCalls
    115                         false, // hasRingingCalls
    116                         false, // hasHoldingCalls
    117                         false, // isTonePlaying
    118                         false, // foregroundCallIsVoip
    119                         null // session
    120                 );
    121         assertMessageArgEquality(correctArgs, captor.getValue());
    122         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    123                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
    124         assertMessageArgEquality(correctArgs, captor.getValue());
    125 
    126         // Mute the current ongoing call.
    127         mCallAudioManager.mute(true);
    128 
    129         // Create a second incoming call.
    130         Call call2 = mock(Call.class);
    131         when(call2.getState()).thenReturn(CallState.RINGING);
    132         when(call2.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
    133                 .thenReturn(false);
    134         when(call2.getId()).thenReturn("2");
    135         mCallAudioManager.onCallAdded(call2);
    136 
    137         // Answer the incoming call
    138         mCallAudioManager.onIncomingCallAnswered(call);
    139 
    140         // Capture the calls to sendMessageWithSessionInfo; we want to look for mute on and off
    141         // messages and make sure that there was a mute on before the mute off.
    142         ArgumentCaptor<Integer> muteCaptor = ArgumentCaptor.forClass(Integer.class);
    143         verify(mCallAudioRouteStateMachine, atLeastOnce())
    144                 .sendMessageWithSessionInfo(muteCaptor.capture());
    145         List<Integer> values = muteCaptor.getAllValues();
    146         values = values.stream()
    147                 .filter(value -> value == CallAudioRouteStateMachine.MUTE_ON ||
    148                         value == CallAudioRouteStateMachine.MUTE_OFF)
    149                 .collect(Collectors.toList());
    150 
    151         // Make sure we got a mute on and a mute off.
    152         assertTrue(values.contains(CallAudioRouteStateMachine.MUTE_ON));
    153         assertTrue(values.contains(CallAudioRouteStateMachine.MUTE_OFF));
    154         // And that the mute on happened before the off.
    155         assertTrue(values.indexOf(CallAudioRouteStateMachine.MUTE_ON) < values
    156                 .lastIndexOf(CallAudioRouteStateMachine.MUTE_OFF));
    157     }
    158 
    159     @MediumTest
    160     @Test
    161     public void testSingleIncomingCallFlowWithoutMTSpeedUp() {
    162         Call call = createIncomingCall();
    163         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
    164                 .thenReturn(false);
    165 
    166         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
    167                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
    168         // Answer the incoming call
    169         mCallAudioManager.onIncomingCallAnswered(call);
    170         when(call.getState()).thenReturn(CallState.ACTIVE);
    171         mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE);
    172         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    173                 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
    174         CallAudioModeStateMachine.MessageArgs correctArgs =
    175                 new CallAudioModeStateMachine.MessageArgs(
    176                         true, // hasActiveOrDialingCalls
    177                         false, // hasRingingCalls
    178                         false, // hasHoldingCalls
    179                         false, // isTonePlaying
    180                         false, // foregroundCallIsVoip
    181                         null // session
    182                 );
    183         assertMessageArgEquality(correctArgs, captor.getValue());
    184         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    185                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
    186         assertMessageArgEquality(correctArgs, captor.getValue());
    187 
    188         disconnectCall(call);
    189         stopTone();
    190 
    191         mCallAudioManager.onCallRemoved(call);
    192         verifyProperCleanup();
    193     }
    194 
    195     @MediumTest
    196     @Test
    197     public void testSingleIncomingCallFlowWithMTSpeedUp() {
    198         Call call = createIncomingCall();
    199         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
    200                 .thenReturn(true);
    201 
    202         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
    203                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
    204         // Answer the incoming call
    205         mCallAudioManager.onIncomingCallAnswered(call);
    206         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    207                 eq(CallAudioModeStateMachine.MT_AUDIO_SPEEDUP_FOR_RINGING_CALL), captor.capture());
    208         CallAudioModeStateMachine.MessageArgs correctArgs =
    209                 new CallAudioModeStateMachine.MessageArgs(
    210                         true, // hasActiveOrDialingCalls
    211                         false, // hasRingingCalls
    212                         false, // hasHoldingCalls
    213                         false, // isTonePlaying
    214                         false, // foregroundCallIsVoip
    215                         null // session
    216                 );
    217         assertMessageArgEquality(correctArgs, captor.getValue());
    218         assertMessageArgEquality(correctArgs, captor.getValue());
    219         when(call.getState()).thenReturn(CallState.ACTIVE);
    220         mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE);
    221 
    222         disconnectCall(call);
    223         stopTone();
    224 
    225         mCallAudioManager.onCallRemoved(call);
    226         verifyProperCleanup();
    227     }
    228 
    229     @MediumTest
    230     @Test
    231     public void testSingleOutgoingCall() {
    232         Call call = mock(Call.class);
    233         when(call.getState()).thenReturn(CallState.CONNECTING);
    234 
    235         mCallAudioManager.onCallAdded(call);
    236         assertEquals(call, mCallAudioManager.getForegroundCall());
    237         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
    238                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
    239         verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
    240                 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
    241         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    242                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
    243         CallAudioModeStateMachine.MessageArgs expectedArgs =
    244                 new CallAudioModeStateMachine.MessageArgs(
    245                         true, // hasActiveOrDialingCalls
    246                         false, // hasRingingCalls
    247                         false, // hasHoldingCalls
    248                         false, // isTonePlaying
    249                         false, // foregroundCallIsVoip
    250                         null // session
    251                 );
    252         assertMessageArgEquality(expectedArgs, captor.getValue());
    253 
    254         when(call.getState()).thenReturn(CallState.DIALING);
    255         mCallAudioManager.onCallStateChanged(call, CallState.CONNECTING, CallState.DIALING);
    256         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
    257                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
    258         assertMessageArgEquality(expectedArgs, captor.getValue());
    259         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
    260                 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
    261 
    262 
    263         when(call.getState()).thenReturn(CallState.ACTIVE);
    264         mCallAudioManager.onCallStateChanged(call, CallState.DIALING, CallState.ACTIVE);
    265         verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
    266                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
    267         assertMessageArgEquality(expectedArgs, captor.getValue());
    268         verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
    269                 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
    270 
    271         disconnectCall(call);
    272         stopTone();
    273 
    274         mCallAudioManager.onCallRemoved(call);
    275         verifyProperCleanup();
    276     }
    277 
    278     private Call createIncomingCall() {
    279         Call call = mock(Call.class);
    280         when(call.getState()).thenReturn(CallState.RINGING);
    281 
    282         mCallAudioManager.onCallAdded(call);
    283         assertEquals(call, mCallAudioManager.getForegroundCall());
    284         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
    285                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
    286         verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
    287                 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
    288         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    289                 eq(CallAudioModeStateMachine.NEW_RINGING_CALL), captor.capture());
    290         assertMessageArgEquality(new CallAudioModeStateMachine.MessageArgs(
    291                 false, // hasActiveOrDialingCalls
    292                 true, // hasRingingCalls
    293                 false, // hasHoldingCalls
    294                 false, // isTonePlaying
    295                 false, // foregroundCallIsVoip
    296                 null // session
    297         ), captor.getValue());
    298 
    299         return call;
    300     }
    301 
    302     private void disconnectCall(Call call) {
    303         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
    304                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
    305         CallAudioModeStateMachine.MessageArgs correctArgs;
    306 
    307         when(call.getState()).thenReturn(CallState.DISCONNECTED);
    308         when(call.getDisconnectCause()).thenReturn(new DisconnectCause(DisconnectCause.LOCAL,
    309                 "", "", "", ToneGenerator.TONE_PROP_PROMPT));
    310 
    311         mCallAudioManager.onCallStateChanged(call, CallState.ACTIVE, CallState.DISCONNECTED);
    312         verify(mPlayerFactory).createPlayer(InCallTonePlayer.TONE_CALL_ENDED);
    313         correctArgs = new CallAudioModeStateMachine.MessageArgs(
    314                 false, // hasActiveOrDialingCalls
    315                 false, // hasRingingCalls
    316                 false, // hasHoldingCalls
    317                 true, // isTonePlaying
    318                 false, // foregroundCallIsVoip
    319                 null // session
    320         );
    321         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    322                 eq(CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS), captor.capture());
    323         assertMessageArgEquality(correctArgs, captor.getValue());
    324         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    325                 eq(CallAudioModeStateMachine.TONE_STARTED_PLAYING), captor.capture());
    326         assertMessageArgEquality(correctArgs, captor.getValue());
    327     }
    328 
    329     private void stopTone() {
    330         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
    331                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
    332         mCallAudioManager.setIsTonePlaying(false);
    333         CallAudioModeStateMachine.MessageArgs correctArgs =
    334                 new CallAudioModeStateMachine.MessageArgs(
    335                         false, // hasActiveOrDialingCalls
    336                         false, // hasRingingCalls
    337                         false, // hasHoldingCalls
    338                         false, // isTonePlaying
    339                         false, // foregroundCallIsVoip
    340                         null // session
    341                 );
    342         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
    343                 eq(CallAudioModeStateMachine.TONE_STOPPED_PLAYING), captor.capture());
    344         assertMessageArgEquality(correctArgs, captor.getValue());
    345     }
    346 
    347     private void verifyProperCleanup() {
    348         assertEquals(0, mCallAudioManager.getTrackedCalls().size());
    349         SparseArray<LinkedHashSet<Call>> callStateToCalls = mCallAudioManager.getCallStateToCalls();
    350         for (int i = 0; i < callStateToCalls.size(); i++) {
    351             assertEquals(0, callStateToCalls.valueAt(i).size());
    352         }
    353     }
    354 
    355     private void assertMessageArgEquality(CallAudioModeStateMachine.MessageArgs expected,
    356             CallAudioModeStateMachine.MessageArgs actual) {
    357         assertEquals(expected.hasActiveOrDialingCalls, actual.hasActiveOrDialingCalls);
    358         assertEquals(expected.hasHoldingCalls, actual.hasHoldingCalls);
    359         assertEquals(expected.hasRingingCalls, actual.hasRingingCalls);
    360         assertEquals(expected.isTonePlaying, actual.isTonePlaying);
    361         assertEquals(expected.foregroundCallIsVoip, actual.foregroundCallIsVoip);
    362     }
    363 }
    364