blob: c659d5efa9de5ff3b80275652f8a2734762952f8 [file] [log] [blame]
package com.android.services.telephony;
import static android.telecom.Connection.STATE_DISCONNECTED;
import static android.telephony.ims.ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
import static android.telephony.ims.ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.telecom.Connection;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.d2d.DtmfTransport;
import com.android.internal.telephony.d2d.RtpTransport;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.phone.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class TelephonyConnectionTest {
@Mock
private ImsPhoneConnection mImsPhoneConnection;
@Mock
private TelephonyConnectionService mTelephonyConnectionService;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mImsPhoneConnection.getState()).thenReturn(Call.State.ACTIVE);
when(mImsPhoneConnection.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
}
/**
* Ensures an Ims connection uses the D2D communicator when it is enabled.
*/
@Test
public void testSetupCommunicator() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
// Enable D2D comms.
when(c.mMockResources.getBoolean(eq(
R.bool.config_use_device_to_device_communication))).thenReturn(true);
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
true);
c.setOriginalConnection(mImsPhoneConnection);
assertNotNull(c.getCommunicator());
}
/**
* Ensures an Ims connection does not use the D2D communicator when it is disabled.
*/
@Test
public void testDoNotSetupCommunicatorWhenDisabled() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
// Disable D2D comms.
when(c.mMockResources.getBoolean(eq(
R.bool.config_use_device_to_device_communication))).thenReturn(false);
c.setOriginalConnection(mImsPhoneConnection);
assertNull(c.getCommunicator());
}
/**
* Ensures an Ims connection does not use the D2D communicator for a non-IMS call.
*/
@Test
public void testDoNotSetupCommunicatorForNonIms() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(false);
// Disable D2D comms.
when(c.mMockResources.getBoolean(eq(
R.bool.config_use_device_to_device_communication))).thenReturn(true);
c.setOriginalConnection(mImsPhoneConnection);
assertNull(c.getCommunicator());
}
/**
* Ensures an Ims connection does not use the D2D communicator when it is disabled.
*/
@Test
public void testDoNotSetupCommunicatorNoTransports() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
// Enable D2D comms.
when(c.mMockResources.getBoolean(eq(
R.bool.config_use_device_to_device_communication))).thenReturn(true);
// But carrier disables transports. Womp.
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
false);
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
false);
c.setOriginalConnection(mImsPhoneConnection);
assertNull(c.getCommunicator());
}
@Test
public void testSetupRtpOnly() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
// Enable D2D comms.
when(c.mMockResources.getBoolean(eq(
R.bool.config_use_device_to_device_communication))).thenReturn(true);
// But carrier disables transports. Womp.
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
false);
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
true);
c.setOriginalConnection(mImsPhoneConnection);
assertNotNull(c.getCommunicator());
assertEquals(1, c.getCommunicator().getTransportProtocols().size());
assertTrue(c.getCommunicator().getTransportProtocols()
.stream().anyMatch(p -> p instanceof RtpTransport));
}
@Test
public void testHangupAfterRedial() throws Exception {
TestTelephonyConnection c = new TestTelephonyConnection();
c.hangup(DisconnectCause.LOCAL);
verify(c.mMockRadioConnection).hangup();
// hangup failed because redial was in progress... The new original connection has been sent
// to the TelephonyConnection
com.android.internal.telephony.Connection newMockRadioConnection =
mock(com.android.internal.telephony.Connection.class);
doReturn("5551212").when(c.mMockRadioConnection).getAddress();
doReturn("5551212").when(newMockRadioConnection).getAddress();
doReturn(Call.State.DIALING).when(newMockRadioConnection).getState();
c.onOriginalConnectionRedialed(newMockRadioConnection);
verify(newMockRadioConnection).hangup();
}
@Test
public void testSetupDtmfOnly() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
// Enable D2D comms.
when(c.mMockResources.getBoolean(eq(
R.bool.config_use_device_to_device_communication))).thenReturn(true);
// But carrier disables transports. Womp.
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
true);
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
false);
c.setOriginalConnection(mImsPhoneConnection);
assertNotNull(c.getCommunicator());
assertEquals(1, c.getCommunicator().getTransportProtocols().size());
assertTrue(c.getCommunicator().getTransportProtocols()
.stream().anyMatch(p -> p instanceof DtmfTransport));
}
@Test
public void testCodecInIms() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
c.updateState();
Bundle extras = c.getExtras();
int codec = extras.getInt(Connection.EXTRA_AUDIO_CODEC, Connection.AUDIO_CODEC_NONE);
assertEquals(codec, Connection.AUDIO_CODEC_AMR);
}
@Test
public void testConferenceNotSupportedForDownGradedVideoCall() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
c.setIsVideoCall(false);
c.setWasVideoCall(true);
c.setDownGradeVideoCall(true);
c.refreshConferenceSupported();
assertFalse(c.isConferenceSupported());
c.setDownGradeVideoCall(false);
c.refreshConferenceSupported();
assertTrue(c.isConferenceSupported());
}
/**
* Tests to ensure that the presence of an ImsExternalConnection does not cause a crash in
* TelephonyConnection due to an illegal cast.
*/
@Test
public void testImsExternalConnectionOnRefreshConference() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setIsImsConnection(true);
c.setIsImsExternalConnection(true);
try {
c.refreshConferenceSupported();
} catch (ClassCastException e) {
fail("refreshConferenceSupported threw ClassCastException");
}
}
/**
* Tests TelephonyConnection#getCarrierConfig never returns a null given all cases that can
* cause a potential null.
*/
@Test
public void testGetCarrierConfigBehaviorWithNull() throws Exception {
TestTelephonyConnectionSimple c = new TestTelephonyConnectionSimple();
// case: return a valid carrier config (good case)
when(c.mPhoneGlobals.getCarrierConfigForSubId(c.getPhone().getSubId())).
thenReturn(CarrierConfigManager.getDefaultConfig());
assertNotNull(c.getCarrierConfig());
// case: PhoneGlobals.getInstance().getCarrierConfigForSubId(int) returns null
when(c.mPhoneGlobals.getCarrierConfigForSubId(c.getPhone().getSubId()))
.thenReturn(null);
assertNotNull(c.getCarrierConfig());
// case: phone is null
c.setMockPhone(null);
assertNull(c.getPhone());
assertNotNull(c.getCarrierConfig());
}
/**
* Tests the behavior of TelephonyConnection#isRttMergeSupported(@NonNull PersistableBundle).
* Note, the function should be able to handle an empty PersistableBundle and should NEVER
* receive a null object as denoted in by @NonNull annotation.
*/
@Test
public void testIsRttMergeSupportedBehavior() {
TestTelephonyConnection c = new TestTelephonyConnection();
// ensure isRttMergeSupported(PersistableBundle) does not throw NPE when given an Empty PB
assertFalse(c.isRttMergeSupported(new PersistableBundle()));
// simulate the passing situation
c.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL,
true);
assertTrue(c.isRttMergeSupported(c.getCarrierConfig()));
}
@Test
public void testDomainSelectionDisconnected() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setOriginalConnection(mImsPhoneConnection);
doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
.getState();
c.setTelephonyConnectionService(mTelephonyConnectionService);
c.updateState();
verify(mTelephonyConnectionService)
.maybeReselectDomain(any(), any(), anyBoolean(), anyInt());
}
@Test
public void testDomainSelectionDisconnected_NoRedial() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setOriginalConnection(mImsPhoneConnection);
doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
.getState();
c.setTelephonyConnectionService(mTelephonyConnectionService);
doReturn(false).when(mTelephonyConnectionService)
.maybeReselectDomain(any(), any(), anyBoolean(), anyInt());
c.updateState();
assertEquals(STATE_DISCONNECTED, c.getState());
}
@Test
public void testDomainSelectionDisconnected_Redial() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setOriginalConnection(mImsPhoneConnection);
doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
.getState();
c.setTelephonyConnectionService(mTelephonyConnectionService);
doReturn(true).when(mTelephonyConnectionService)
.maybeReselectDomain(any(), any(), anyBoolean(), anyInt());
c.resetOriginalConnectionCleared();
c.updateState();
assertNotEquals(STATE_DISCONNECTED, c.getState());
assertTrue(c.isOriginalConnectionCleared());
}
@Test
public void testDomainSelectionDisconnected_AlternateService() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setOriginalConnection(mImsPhoneConnection);
c.setIsImsConnection(true);
doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
.getState();
doReturn(new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null))
.when(mImsPhoneConnection).getImsReasonInfo();
doReturn(getEmergencyNumber(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE))
.when(mImsPhoneConnection).getEmergencyNumberInfo();
c.setTelephonyConnectionService(mTelephonyConnectionService);
doReturn(true).when(mTelephonyConnectionService)
.maybeReselectDomain(any(), any(), anyBoolean(), anyInt());
c.updateState();
Integer serviceCategory = c.getEmergencyServiceCategory();
assertNotNull(serviceCategory);
assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
serviceCategory.intValue());
}
@Test
public void testDomainSelectionDisconnected_SilentRedialEmergency() {
TestTelephonyConnection c = new TestTelephonyConnection();
c.setOriginalConnection(mImsPhoneConnection);
c.setIsImsConnection(true);
doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
.getState();
doReturn(new ImsReasonInfo(CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
EXTRA_CODE_CALL_RETRY_EMERGENCY, null))
.when(mImsPhoneConnection).getImsReasonInfo();
doReturn(getEmergencyNumber(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE))
.when(mImsPhoneConnection).getEmergencyNumberInfo();
c.setTelephonyConnectionService(mTelephonyConnectionService);
doReturn(true).when(mTelephonyConnectionService)
.maybeReselectDomain(any(), any(), anyBoolean(), anyInt());
c.updateState();
Integer serviceCategory = c.getEmergencyServiceCategory();
assertNotNull(serviceCategory);
assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
serviceCategory.intValue());
}
private EmergencyNumber getEmergencyNumber(int eccCategory) {
return new EmergencyNumber("", "", "", eccCategory,
new ArrayList<String>(),
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
}
}