blob: a4743c96ea679e4fa442dd7128c570cfa71c907b [file] [log] [blame]
/*
* Copyright (C) 2016 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 com.android.services.telephony;
import static android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
import static android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
import static android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
import static android.telephony.DisconnectCause.NOT_DISCONNECTED;
import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
import static com.android.internal.telephony.RILConstants.GSM_PHONE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
import android.telecom.Conference;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.DomainSelectionService;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.TelephonyTestBase;
import com.android.ims.ImsManager;
import com.android.internal.telecom.IConnectionService;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.data.PhoneSwitcher;
import com.android.internal.telephony.domainselection.DomainSelectionConnection;
import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.domainselection.EmergencyCallDomainSelectionConnection;
import com.android.internal.telephony.domainselection.NormalCallDomainSelectionConnection;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.imsphone.ImsPhone;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
/**
* Unit tests for TelephonyConnectionService.
*/
@RunWith(AndroidJUnit4.class)
public class TelephonyConnectionServiceTest extends TelephonyTestBase {
/**
* Unlike {@link TestTelephonyConnection}, a bare minimal {@link TelephonyConnection} impl
* that does not try to configure anything.
*/
public static class SimpleTelephonyConnection extends TelephonyConnection {
public boolean wasDisconnected = false;
public boolean wasUnheld = false;
@Override
public TelephonyConnection cloneConnection() {
return null;
}
@Override
public void hangup(int telephonyDisconnectCode) {
wasDisconnected = true;
}
@Override
public void onUnhold() {
wasUnheld = true;
}
}
public static class SimpleConference extends Conference {
public boolean wasUnheld = false;
public SimpleConference(PhoneAccountHandle phoneAccountHandle) {
super(phoneAccountHandle);
}
@Override
public void onUnhold() {
wasUnheld = true;
}
}
private static final long TIMEOUT_MS = 100;
private static final int SLOT_0_PHONE_ID = 0;
private static final int SLOT_1_PHONE_ID = 1;
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
"com.android.phone.tests", TelephonyConnectionServiceTest.class.getName());
private static final String TEST_ACCOUNT_ID1 = "id1";
private static final String TEST_ACCOUNT_ID2 = "id2";
private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_1 = new PhoneAccountHandle(
TEST_COMPONENT_NAME, TEST_ACCOUNT_ID1);
private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
private static final Uri TEST_ADDRESS = Uri.parse("tel:+16505551212");
private static final String TELECOM_CALL_ID1 = "TC1";
private static final String TEST_EMERGENCY_NUMBER = "911";
private android.telecom.Connection mConnection;
@Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
@Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
@Mock TelephonyConnectionService.PhoneFactoryProxy mPhoneFactoryProxy;
@Mock DeviceState mDeviceState;
@Mock TelephonyConnectionService.PhoneSwitcherProxy mPhoneSwitcherProxy;
@Mock TelephonyConnectionService.PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
@Mock TelephonyConnectionService.PhoneUtilsProxy mPhoneUtilsProxy;
@Mock TelephonyConnectionService.DisconnectCauseFactory mDisconnectCauseFactory;
@Mock Handler mMockHandler;
@Mock EmergencyNumberTracker mEmergencyNumberTracker;
@Mock PhoneSwitcher mPhoneSwitcher;
@Mock RadioOnHelper mRadioOnHelper;
@Mock ServiceStateTracker mSST;
@Mock Call mCall;
@Mock Call mCall2;
@Mock com.android.internal.telephony.Connection mInternalConnection;
@Mock com.android.internal.telephony.Connection mInternalConnection2;
@Mock DomainSelectionResolver mDomainSelectionResolver;
@Mock EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
@Mock NormalCallDomainSelectionConnection mNormalCallDomainSelectionConnection;
@Mock ImsPhone mImsPhone;
private EmergencyStateTracker mEmergencyStateTracker;
private Phone mPhone0;
private Phone mPhone1;
private static class TestTelephonyConnectionService extends TelephonyConnectionService {
private final Context mContext;
TestTelephonyConnectionService(Context context) {
mContext = context;
}
@Override
public void onCreate() {
// attach test context.
attachBaseContext(mContext);
super.onCreate();
}
}
private TelephonyConnectionService mTestConnectionService;
private IConnectionService.Stub mBinderStub;
@Before
public void setUp() throws Exception {
super.setUp();
mTestConnectionService = new TestTelephonyConnectionService(mContext);
mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
// Set configurations statically
doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
when(mPhoneNumberUtilsProxy.convertToEmergencyNumber(any(), anyString()))
.thenAnswer(invocation -> invocation.getArgument(1));
mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
mTestConnectionService.setDeviceState(mDeviceState);
mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
.toTelecomDisconnectCause(anyInt(), any());
doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
.toTelecomDisconnectCause(anyInt(), any(), anyInt());
mTestConnectionService.setDisconnectCauseFactory(mDisconnectCauseFactory);
mTestConnectionService.onCreate();
mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
replaceInstance(TelephonyConnectionService.class, "mDomainSelectionResolver",
mTestConnectionService, mDomainSelectionResolver);
mEmergencyStateTracker = Mockito.mock(EmergencyStateTracker.class);
replaceInstance(TelephonyConnectionService.class, "mEmergencyStateTracker",
mTestConnectionService, mEmergencyStateTracker);
doReturn(CompletableFuture.completedFuture(NOT_DISCONNECTED))
.when(mEmergencyStateTracker)
.startEmergencyCall(any(), anyString(), eq(false));
replaceInstance(TelephonyConnectionService.class,
"mDomainSelectionMainExecutor", mTestConnectionService, getExecutor());
doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
doReturn(null).when(mDomainSelectionResolver).getDomainSelectionConnection(
any(), anyInt(), anyBoolean());
mBinderStub = (IConnectionService.Stub) mTestConnectionService.onBind(null);
}
@After
public void tearDown() throws Exception {
mTestConnectionService = null;
super.tearDown();
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 0 is IN_SERVICE, Slot 1 is OUT_OF_SERVICE (emergency calls only)
* - Slot 1 is in Emergency SMS Mode
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone
*/
@Test
@SmallTest
public void testEmergencySmsModeSimEmergencyOnly() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
true /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setEmergencySmsMode(slot1Phone, true);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 0 is IN_SERVICE, Slot 1 is OUT_OF_SERVICE
* - Slot 1 is in Emergency SMS Mode
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone
*/
@Test
@SmallTest
public void testEmergencySmsModeSimOutOfService() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setEmergencySmsMode(slot1Phone, true);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Users default Voice SIM choice is IN_SERVICE
*
* Result: getFirstPhoneForEmergencyCall returns the default Voice SIM choice.
*/
@Test
@SmallTest
public void testDefaultVoiceSimInService() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
true /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Users default data SIM choice is OUT_OF_SERVICE (emergency calls only)
*
* Result: getFirstPhoneForEmergencyCall returns the default data SIM choice.
*/
@Test
@SmallTest
public void testDefaultDataSimEmergencyOnly() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
true /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
setDefaultDataPhoneId(SLOT_1_PHONE_ID);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Users default data SIM choice is OUT_OF_SERVICE
*
* Result: getFirstPhoneForEmergencyCall does not return the default data SIM choice.
*/
@Test
@SmallTest
public void testDefaultDataSimOutOfService() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
setDefaultDataPhoneId(SLOT_1_PHONE_ID);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 0 is OUT_OF_SERVICE, Slot 1 is OUT_OF_SERVICE (emergency calls only)
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone
*/
@Test
@SmallTest
public void testSlot1EmergencyOnly() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
true /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 0 is OUT_OF_SERVICE, Slot 1 is IN_SERVICE
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone
*/
@Test
@SmallTest
public void testSlot1InService() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 0 is PUK locked, Slot 1 is ready
* - Slot 0 is LTE capable, Slot 1 is GSM capable
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone. Although Slot 0 is more
* capable, it is locked, so use the other slot.
*/
@Test
@SmallTest
public void testSlot0PukLocked() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
// Set Slot 0 to be PUK locked
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_PUK_REQUIRED);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Slot 0 higher capability
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_GSM);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 0 is PIN locked, Slot 1 is ready
* - Slot 0 is LTE capable, Slot 1 is GSM capable
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone. Although Slot 0 is more
* capable, it is locked, so use the other slot.
*/
@Test
@SmallTest
public void testSlot0PinLocked() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
// Set Slot 0 to be PUK locked
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_PIN_REQUIRED);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Slot 0 higher capability
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_GSM);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 1 is PUK locked, Slot 0 is ready
* - Slot 1 is LTE capable, Slot 0 is GSM capable
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone. Although Slot 1 is more
* capable, it is locked, so use the other slot.
*/
@Test
@SmallTest
public void testSlot1PukLocked() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
// Set Slot 1 to be PUK locked
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_PUK_REQUIRED);
// Make Slot 1 higher capability
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 1 is PIN locked, Slot 0 is ready
* - Slot 1 is LTE capable, Slot 0 is GSM capable
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone. Although Slot 1 is more
* capable, it is locked, so use the other slot.
*/
@Test
@SmallTest
public void testSlot1PinLocked() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
// Set Slot 1 to be PUK locked
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_PIN_REQUIRED);
// Make Slot 1 higher capability
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, only slot 1 inserted and PUK locked
* - slot 1 has higher capabilities
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it is the only one
* with a SIM inserted (even if it is PUK locked)
*/
@Test
@SmallTest
public void testSlot1PinLockedAndSlot0Absent() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_PIN_REQUIRED);
// Slot 1 has more capabilities
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 1 is LTE capable, Slot 0 is GSM capable
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it is more capable
*/
@Test
@SmallTest
public void testSlot1HigherCapablity() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Slot 1 higher capability
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 1 is GSM/LTE capable, Slot 0 is GSM capable
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it has more
* capabilities.
*/
@Test
@SmallTest
public void testSlot1MoreCapabilities() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Slot 1 more capable
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone,
RadioAccessFamily.RAF_GSM | RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Both SIMs PUK Locked
* - Slot 0 is LTE capable, Slot 1 is GSM capable
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone because it is more capable,
* ignoring that both SIMs are PUK locked.
*/
@Test
@SmallTest
public void testSlot0MoreCapableBothPukLocked() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_PUK_REQUIRED);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_PUK_REQUIRED);
// Make Slot 0 higher capability
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_GSM);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Both SIMs have the same capability
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone because it is the first slot.
*/
@Test
@SmallTest
public void testEqualCapabilityTwoSimsInserted() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, only slot 0 inserted
* - Both SIMs have the same capability
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone because it is the only one
* with a SIM inserted
*/
@Test
@SmallTest
public void testEqualCapabilitySim0Inserted() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, only slot 1 inserted
* - Both SIMs have the same capability
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it is the only one
* with a SIM inserted
*/
@Test
@SmallTest
public void testEqualCapabilitySim1Inserted() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device with one ESIM, only slot 1 inserted has PSIM inserted
* - Both phones have the same capability
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it is the only one
* with a SIM inserted
*/
@Test
@SmallTest
public void testEqualCapabilitySim1Inserted_WithOneEsim() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
when(slot0Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_LTE);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, no SIMs inserted
* - SIM 1 has the higher capability
*
* Result: getFirstPhoneForEmergencyCall returns the slot 1 phone, since it is a higher
* capability
*/
@Test
@SmallTest
public void testSim1HigherCapabilityNoSimsInserted() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot1Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, no SIMs inserted
* - Both SIMs have the same capability (Unknown)
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone, since it is the first slot.
*/
@Test
@SmallTest
public void testEqualCapabilityNoSimsInserted() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
// Make Capability the same
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_UNKNOWN);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_UNKNOWN);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, no SIMs inserted (one ESIM)
* - Both SIMs have the same capability (Unknown)
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone, since it is the first slot.
*/
@Test
@SmallTest
public void testEqualCapabilityNoSimsInserted_WithOneESim() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
when(slot1Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Capability the samesvim
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_UNKNOWN);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_UNKNOWN);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* Prerequisites:
* - MSIM Device, both ESIMS (no profile activated)
* - Both phones have the same capability (Unknown)
*
* Result: getFirstPhoneForEmergencyCall returns the slot 0 phone, since it is the first slot.
*/
@Test
@SmallTest
public void testEqualCapabilityNoSimsInserted_WithTwoESims() {
Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setDefaultPhone(slot0Phone);
setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
when(slot0Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_READY);
when(slot1Phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_READY);
// Make Capability the sames
setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_UNKNOWN);
setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_UNKNOWN);
Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
assertEquals(slot0Phone, resultPhone);
}
/**
* The modem has returned a temporary error when placing an emergency call on a phone with one
* SIM slot.
*
* Verify that dial is called on the same phone again when retryOutgoingOriginalConnection is
* called.
*/
@Test
@SmallTest
public void testRetryOutgoingOriginalConnection_redialTempFailOneSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
Phone slot0Phone = c.getPhone();
when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
List<Phone> phones = new ArrayList<>(1);
phones.add(slot0Phone);
setPhones(phones);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), false /*isPermanentFailure*/);
// We never need to be notified in telecom that the PhoneAccount has changed, because it
// was redialed on the same slot
assertEquals(0, c.getNotifyPhoneAccountChangedCount());
try {
verify(slot0Phone).dial(anyString(), any(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
}
/**
* The modem has returned a permanent failure when placing an emergency call on a phone with one
* SIM slot.
*
* Verify that the connection is set to disconnected with an error disconnect cause and dial is
* not called.
*/
@Test
@SmallTest
public void testRetryOutgoingOriginalConnection_redialPermFailOneSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
Phone slot0Phone = c.getPhone();
when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
List<Phone> phones = new ArrayList<>(1);
phones.add(slot0Phone);
setPhones(phones);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), true /*isPermanentFailure*/);
// We never need to be notified in telecom that the PhoneAccount has changed, because it
// was never redialed
assertEquals(0, c.getNotifyPhoneAccountChangedCount());
try {
verify(slot0Phone, never()).dial(anyString(), any(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
assertEquals(c.getState(), android.telecom.Connection.STATE_DISCONNECTED);
assertEquals(c.getDisconnectCause().getCode(), DisconnectCause.ERROR);
}
/**
* The modem has returned a temporary failure when placing an emergency call on a phone with two
* SIM slots.
*
* Verify that the emergency call is dialed on the other slot and telecom is notified of the new
* PhoneAccount.
*/
@Test
@SmallTest
public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
Phone slot0Phone = c.getPhone();
when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot0Phone);
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), false /*isPermanentFailure*/);
// The cache should still contain all of the Phones, since it was a temporary failure.
assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
// We need to be notified in Telecom that the PhoneAccount has changed, because it was
// redialed on another slot
assertEquals(1, c.getNotifyPhoneAccountChangedCount());
try {
verify(slot1Phone).dial(anyString(), any(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
}
/**
* The modem has returned a temporary failure when placing an emergency call on a phone with two
* SIM slots.
*
* Verify that the emergency call is dialed on the other slot and telecom is notified of the new
* PhoneAccount.
*/
@Test
@SmallTest
public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
Phone slot0Phone = c.getPhone();
when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot0Phone);
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), true /*isPermanentFailure*/);
// The cache should only contain the slot1Phone.
assertEquals(1, mTestConnectionService.mEmergencyRetryCache.second.size());
// We need to be notified in Telecom that the PhoneAccount has changed, because it was
// redialed on another slot
assertEquals(1, c.getNotifyPhoneAccountChangedCount());
try {
verify(slot1Phone).dial(anyString(), any(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
}
/**
* The modem has returned a temporary failure twice while placing an emergency call on a phone
* with two SIM slots.
*
* Verify that the emergency call is dialed on slot 1 and then on slot 0 and telecom is
* notified of this twice.
*/
@Test
@SmallTest
public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot_twoFailure() {
TestTelephonyConnection c = new TestTelephonyConnection();
Phone slot0Phone = c.getPhone();
when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot0Phone);
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
// First Temporary failure
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), false /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot1Phone);
// The cache should still contain all of the Phones, since it was a temporary failure.
assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
// Make sure slot 1 is next in the queue.
assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
// Second Temporary failure
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), false /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot0Phone);
// The cache should still contain all of the Phones, since it was a temporary failure.
assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
// Make sure slot 0 is next in the queue.
assertEquals(slot0Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
// We need to be notified in Telecom that the PhoneAccount has changed, because it was
// redialed on another slot
assertEquals(2, c.getNotifyPhoneAccountChangedCount());
try {
verify(slot0Phone).dial(anyString(), any(), any());
verify(slot1Phone).dial(anyString(), any(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
}
/**
* The modem has returned a permanent failure twice while placing an emergency call on a phone
* with two SIM slots.
*
* Verify that the emergency call is dialed on slot 1 and then disconnected and telecom is
* notified of the change to slot 1.
*/
@Test
@SmallTest
public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot_twoFailure() {
TestTelephonyConnection c = new TestTelephonyConnection();
Phone slot0Phone = c.getPhone();
when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot0Phone);
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
// First Permanent failure
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), true /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot1Phone);
// The cache should only contain one phone
assertEquals(1, mTestConnectionService.mEmergencyRetryCache.second.size());
// Make sure slot 1 is next in the queue.
assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
// Second Permanent failure
mTestConnectionService.retryOutgoingOriginalConnection(c,
c.getPhone(), true /*isPermanentFailure*/);
// The cache should be empty
assertEquals(true, mTestConnectionService.mEmergencyRetryCache.second.isEmpty());
assertEquals(c.getState(), android.telecom.Connection.STATE_DISCONNECTED);
assertEquals(c.getDisconnectCause().getCode(), DisconnectCause.ERROR);
// We need to be notified in Telecom that the PhoneAccount has changed, because it was
// redialed on another slot
assertEquals(1, c.getNotifyPhoneAccountChangedCount());
try {
verify(slot1Phone).dial(anyString(), any(), any());
verify(slot0Phone, never()).dial(anyString(), any(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
}
@Test
@SmallTest
public void testSuppServiceNotification() {
TestTelephonyConnection c = new TestTelephonyConnection();
// We need to set the original connection to cause the supp service notification
// registration to occur.
Phone phone = c.getPhone();
c.setOriginalConnection(c.getOriginalConnection());
doReturn(mContext).when(phone).getContext();
// When the registration occurs, we'll capture the handler and message so we can post our
// own messages to it.
ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
ArgumentCaptor<Integer> messageCaptor = ArgumentCaptor.forClass(Integer.class);
verify(phone).registerForSuppServiceNotification(handlerCaptor.capture(),
messageCaptor.capture(), any());
Handler handler = handlerCaptor.getValue();
int message = messageCaptor.getValue();
// With the handler and message now known, we'll post a supp service notification.
AsyncResult result = getSuppServiceNotification(
SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
SuppServiceNotification.CODE_1_CALL_FORWARDED);
handler.obtainMessage(message, result).sendToTarget();
waitForHandlerAction(handler, TIMEOUT_MS);
assertTrue(c.getLastConnectionEvents().contains(TelephonyManager.EVENT_CALL_FORWARDED));
// With the handler and message now known, we'll post a supp service notification.
result = getSuppServiceNotification(
SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
SuppServiceNotification.CODE_1_CALL_IS_WAITING);
handler.obtainMessage(message, result).sendToTarget();
waitForHandlerAction(handler, TIMEOUT_MS);
// We we want the 3rd event since the forwarding one above sends 2.
assertEquals(c.getLastConnectionEvents().get(2),
TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION);
Bundle extras = c.getLastConnectionEventExtras().get(2);
assertEquals(SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE));
assertEquals(SuppServiceNotification.CODE_1_CALL_IS_WAITING,
extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_CODE));
}
/**
* Test that the TelephonyConnectionService successfully performs a DDS switch before a call
* when we are not roaming and the carrier only supports SUPL over the data plane.
*/
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_delayDial_carrierconfig_dds() {
// Setup test to not support SUPL on the non-DDS subscription
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "150");
Phone testPhone = setupConnectionServiceForDelayDial(
false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
null /* operator short name */, null /* operator numeric name */);
verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
eq(150) /*extensionTime*/, any());
}
/**
* Test that the TelephonyConnectionService successfully turns radio on before placing the
* emergency call.
*/
@Test
@SmallTest
public void testCreateOutgoingEmerge_exitingApm_disconnected() {
when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
Phone testPhone = setupConnectionServiceInApm();
ArgumentCaptor<RadioOnStateListener.Callback> callback =
ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
eq(testPhone), eq(false));
assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
when(mSST.isRadioOn()).thenReturn(true);
assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
mConnection.setDisconnected(null);
callback.getValue().onComplete(null, true);
for (Phone phone : mPhoneFactoryProxy.getPhones()) {
verify(phone).setRadioPower(true, false, false, true);
}
}
/**
* Test that the TelephonyConnectionService successfully turns radio on before placing the
* emergency call.
*/
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_exitingApm_placeCall() {
when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
Phone testPhone = setupConnectionServiceInApm();
ArgumentCaptor<RadioOnStateListener.Callback> callback =
ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
eq(testPhone), eq(false));
assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
when(mSST.isRadioOn()).thenReturn(true);
assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
callback.getValue().onComplete(null, true);
try {
doAnswer(invocation -> null).when(mContext).startActivity(any());
verify(testPhone).dial(anyString(), any(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
}
/**
* Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
* supports control-plane fallback.
*/
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_delayDial_nocarrierconfig() {
// Setup test to not support SUPL on the non-DDS subscription
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
Phone testPhone = setupConnectionServiceForDelayDial(
false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
null /* operator short name */, null /* operator numeric name */);
verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
}
/**
* Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
* supports control-plane fallback.
*/
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_delayDial_supportsuplondds() {
// If the non-DDS supports SUPL, don't switch data
doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
Phone testPhone = setupConnectionServiceForDelayDial(
false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
null /* operator short name */, null /* operator numeric name */);
verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
}
/**
* Test that the TelephonyConnectionService does not perform a DDS switch when the carrier does
* not support control-plane fallback CarrierConfig while roaming.
*/
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_delayDial_roaming_nocarrierconfig() {
// Setup test to not support SUPL on the non-DDS subscription
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
Phone testPhone = setupConnectionServiceForDelayDial(
true /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
null /* operator short name */, null /* operator numeric name */);
verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
}
/**
* Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
* supports control-plane fallback CarrierConfig and the roaming partner is configured to look
* like a home network.
*/
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_delayDial_roamingcarrierconfig() {
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
// Setup voice roaming scenario
String testRoamingOperator = "001001";
// Setup test to not support SUPL on the non-DDS subscription
String[] roamingPlmns = new String[1];
roamingPlmns[0] = testRoamingOperator;
getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
roamingPlmns);
getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
Phone testPhone = setupConnectionServiceForDelayDial(
false /* isRoaming */, true /* setOperatorName */,
"TestTel" /* operator long name*/, "TestTel" /* operator short name */,
testRoamingOperator /* operator numeric name */);
verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
eq(0) /*extensionTime*/, any());
}
/**
* Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
* supports control-plane fallback CarrierConfig if we are roaming and the roaming partner is
* configured to use data plane only SUPL.
*/
@Test
@SmallTest
public void testCreateOutgoingEmergencyConnection_delayDial__roaming_roamingcarrierconfig() {
// Setup test to not support SUPL on the non-DDS subscription
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
// Setup voice roaming scenario
String testRoamingOperator = "001001";
String[] roamingPlmns = new String[1];
roamingPlmns[0] = testRoamingOperator;
getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
roamingPlmns);
getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
Phone testPhone = setupConnectionServiceForDelayDial(
false /* isRoaming */, true /* setOperatorName */,
"TestTel" /* operator long name*/, "TestTel" /* operator short name */,
testRoamingOperator /* operator numeric name */);
verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
eq(0) /*extensionTime*/, any());
}
/**
* Verifies for an incoming call on the same SIM that we don't set
* {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming call extras.
* @throws Exception
*/
@Test
@SmallTest
public void testIncomingDoesntRequestDisconnect() throws Exception {
setupForCallTest();
mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@1",
new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551212"),
new Bundle()),
true, false, null);
waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
assertEquals(1, mTestConnectionService.getAllConnections().size());
// Make sure the extras do not indicate that it answering will disconnect another call.
android.telecom.Connection connection = (android.telecom.Connection)
mTestConnectionService.getAllConnections().toArray()[0];
assertFalse(connection.getExtras() != null && connection.getExtras().containsKey(
android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL));
}
/**
* Verifies where there is another call on the same sub, we don't set
* {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming call extras.
* @throws Exception
*/
@Test
@SmallTest
public void testSecondCallSameSubWontDisconnect() throws Exception {
// Previous test gets us into a good enough state
testIncomingDoesntRequestDisconnect();
when(mCall.getState()).thenReturn(Call.State.ACTIVE);
when(mCall2.getState()).thenReturn(Call.State.WAITING);
when(mCall2.getLatestConnection()).thenReturn(mInternalConnection2);
when(mPhone0.getRingingCall()).thenReturn(mCall2);
mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@2",
new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551213"),
new Bundle()),
true, false, null);
waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
assertEquals(2, mTestConnectionService.getAllConnections().size());
// None of the connections should have the extra set.
assertEquals(0, mTestConnectionService.getAllConnections().stream()
.filter(c -> c.getExtras() != null && c.getExtras().containsKey(
android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL))
.count());
}
/**
* Verifies where there is another call on a different sub, we set
* {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming call extras.
* @throws Exception
*/
@Test
@SmallTest
public void testSecondCallDifferentSubWillDisconnect() throws Exception {
// Previous test gets us into a good enough state
testIncomingDoesntRequestDisconnect();
when(mCall.getState()).thenReturn(Call.State.ACTIVE);
when(mCall2.getState()).thenReturn(Call.State.WAITING);
when(mCall2.getLatestConnection()).thenReturn(mInternalConnection2);
// At this point the call is ringing on the second phone.
when(mPhone0.getRingingCall()).thenReturn(null);
when(mPhone1.getRingingCall()).thenReturn(mCall2);
mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_2, "TC@2",
new ConnectionRequest(PHONE_ACCOUNT_HANDLE_2, Uri.parse("tel:16505551213"),
new Bundle()),
true, false, null);
waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
assertEquals(2, mTestConnectionService.getAllConnections().size());
// The incoming connection should have the extra set.
assertEquals(1, mTestConnectionService.getAllConnections().stream()
.filter(c -> c.getExtras() != null && c.getExtras().containsKey(
android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL))
.count());
}
/**
* For virtual DSDA-enabled devices, verifies where there is another call on the same sub, we
* don't set {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming
* call extras.
* @throws Exception
*/
@Test
@SmallTest
public void testSecondCallDifferentSubWontDisconnectForDsdaDevice() throws Exception {
// Re-uses existing test for setup, then configures device as virtual DSDA for test duration
testIncomingDoesntRequestDisconnect();
when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
when(mCall.getState()).thenReturn(Call.State.ACTIVE);
when(mCall2.getState()).thenReturn(Call.State.WAITING);
when(mCall2.getLatestConnection()).thenReturn(mInternalConnection2);
// At this point the call is ringing on the second phone.
when(mPhone0.getRingingCall()).thenReturn(null);
when(mPhone1.getRingingCall()).thenReturn(mCall2);
mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_2, "TC@2",
new ConnectionRequest(PHONE_ACCOUNT_HANDLE_2, Uri.parse("tel:16505551213"),
new Bundle()),
true, false, null);
waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
assertEquals(2, mTestConnectionService.getAllConnections().size());
// None of the connections should have the extra set.
assertEquals(0, mTestConnectionService.getAllConnections().stream()
.filter(c -> c.getExtras() != null && c.getExtras().containsKey(
android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL))
.count());
}
private static final PhoneAccountHandle SUB1_HANDLE = new PhoneAccountHandle(
new ComponentName("test", "class"), "1");
private static final PhoneAccountHandle SUB2_HANDLE = new PhoneAccountHandle(
new ComponentName("test", "class"), "2");
@Test
@SmallTest
public void testDontDisconnectSameSub() {
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
tcs, SUB1_HANDLE, mTelephonyManagerProxy);
// Would've preferred to use mockito, but can't mock out TelephonyConnection/Connection
// easily.
assertFalse(tc1.wasDisconnected);
}
@Test
@SmallTest
public void testDontDisconnectEmergency() {
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, true);
tcs.add(tc1);
TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
tcs, SUB2_HANDLE, mTelephonyManagerProxy);
// Other call is an emergency call, so don't disconnect it.
assertFalse(tc1.wasDisconnected);
}
@Test
@SmallTest
public void testDontDisconnectExternal() {
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE,
android.telecom.Connection.PROPERTY_IS_EXTERNAL_CALL, false);
tcs.add(tc1);
TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
tcs, SUB2_HANDLE, mTelephonyManagerProxy);
// Other call is an external call, so don't disconnect it.
assertFalse(tc1.wasDisconnected);
}
@Test
@SmallTest
public void testDisconnectDifferentSub() {
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
tcs, SUB2_HANDLE, mTelephonyManagerProxy);
assertTrue(tc1.wasDisconnected);
}
@Test
@SmallTest
public void testDisconnectDifferentSubTwoCalls() {
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
SimpleTelephonyConnection tc2 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
tcs.add(tc2);
TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
tcs, SUB2_HANDLE, mTelephonyManagerProxy);
assertTrue(tc1.wasDisconnected);
assertTrue(tc2.wasDisconnected);
}
/**
* Verifies that DSDA or virtual DSDA-enabled devices can support active non-emergency calls on
* separate subs.
*/
@Test
@SmallTest
public void testDontDisconnectDifferentSubForVirtualDsdaDevice() {
when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
tcs, SUB2_HANDLE, mTelephonyManagerProxy);
assertFalse(tc1.wasDisconnected);
}
/**
* For calls on the same sub, the Dialer implements the 'swap' functionality to perform hold and
* unhold, so we do not additionally unhold when 'hold' button is pressed.
*/
@Test
@SmallTest
public void testDontUnholdOnSameSubForVirtualDsdaDevice() {
when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
Collection<Conference> conferences = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
TelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
tcs, conferences, SUB1_HANDLE, mTelephonyManagerProxy);
assertFalse(tc1.wasUnheld);
}
/**
* Triggering 'Hold' on 1 call will unhold the other call for DSDA or Virtual DSDA
* enabled devices, effectively constituting 'swap' functionality.
*/
@Test
@SmallTest
public void testUnholdOnOtherSubForVirtualDsdaDevice() {
when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
tcs.add(tc1);
TelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
tcs, new ArrayList<>(), SUB2_HANDLE, mTelephonyManagerProxy);
assertTrue(tc1.wasUnheld);
}
/**
* Verifies hold/unhold behavior for a conference on the other sub. It does not disturb the
* individual connections that participate in the conference.
*/
@Test
@SmallTest
public void testUnholdConferenceOnOtherSubForVirtualDsdaDevice() {
when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
SimpleTelephonyConnection tc1 =
createTestConnection(SUB1_HANDLE, 0, false);
SimpleTelephonyConnection tc2 =
createTestConnection(SUB1_HANDLE, 0, false);
List<android.telecom.Connection> conferenceParticipants = Arrays.asList(tc1, tc2);
SimpleConference testConference = createTestConference(SUB1_HANDLE, 0);
List<Conference> conferences = Arrays.asList(testConference);
TelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
conferenceParticipants, conferences, SUB2_HANDLE, mTelephonyManagerProxy);
assertTrue(testConference.wasUnheld);
assertFalse(tc1.wasUnheld);
assertFalse(tc2.wasUnheld);
}
/**
* Verifies that TelephonyManager is used to determine whether a connection is Emergency when
* creating an outgoing connection.
*/
@Test
@SmallTest
public void testIsEmergencyDeterminedByTelephonyManager() {
ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
.setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
.setAddress(TEST_ADDRESS)
.build();
mConnection = mTestConnectionService.onCreateOutgoingConnection(
PHONE_ACCOUNT_HANDLE_1, connectionRequest);
verify(mTelephonyManagerProxy)
.isCurrentEmergencyNumber(TEST_ADDRESS.getSchemeSpecificPart());
}
@Test
public void testDomainSelectionCs() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_CS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
verify(mEmergencyStateTracker)
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(selectedDomain,
dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
@Test
public void testDomainSelectionPs() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_PS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
verify(mEmergencyStateTracker)
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(selectedDomain,
dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
@Test
public void testDomainSelectionCsForTty() throws Exception {
setupForCallTest();
ImsManager imsManager = Mockito.mock(ImsManager.class);
doReturn(false).when(imsManager).isNonTtyOrTtyOnVolteEnabled();
replaceInstance(TelephonyConnectionService.class,
"mImsManager", mTestConnectionService, imsManager);
setupForDialForDomainSelection(mPhone0, DOMAIN_PS, true);
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
verify(mEmergencyStateTracker, times(1))
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
verify(mDomainSelectionResolver, times(0))
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
verify(mEmergencyCallDomainSelectionConnection, times(0))
.createEmergencyConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(DOMAIN_CS, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
@Test
public void testDomainSelectionRedialCs() throws Exception {
setupForCallTest();
int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
int selectedDomain = DOMAIN_CS;
TestTelephonyConnection c = setupForReDialForDomainSelection(
mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
Connection nc = Mockito.mock(Connection.class);
doReturn(nc).when(mPhone0).dial(anyString(), any(), any());
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(selectedDomain,
dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
@Test
public void testDomainSelectionRedialPs() throws Exception {
setupForCallTest();
int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
int selectedDomain = DOMAIN_PS;
TestTelephonyConnection c = setupForReDialForDomainSelection(
mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
Connection nc = Mockito.mock(Connection.class);
doReturn(nc).when(mPhone0).dial(anyString(), any(), any());
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(selectedDomain,
dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
@Test
public void testDomainSelectionNormalRoutingEmergencyNumber() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_PS;
EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
Collections.emptyList(),
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
setupForDialForDomainSelection(mPhone0, selectedDomain, false);
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(
selectedDomain, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
@Test
public void testDomainSelectionNormalToEmergencyCs() throws Exception {
setupForCallTest();
int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
int selectedDomain = DOMAIN_CS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
TestTelephonyConnection c = setupForReDialForDomainSelection(
mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
c.setEmergencyServiceCategory(eccCategory);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
assertTrue(mTestConnectionService.maybeReselectDomain(c,
preciseDisconnectCause, reasonInfo));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
verify(mEmergencyStateTracker)
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(selectedDomain,
dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
assertTrue(dialArgs.isEmergency);
assertEquals(eccCategory, dialArgs.eccCategory);
}
@Test
public void testDomainSelectionNormalToEmergencyPs() throws Exception {
setupForCallTest();
int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
int selectedDomain = DOMAIN_PS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
TestTelephonyConnection c = setupForReDialForDomainSelection(
mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
c.setEmergencyServiceCategory(eccCategory);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
assertTrue(mTestConnectionService.maybeReselectDomain(c,
preciseDisconnectCause, reasonInfo));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
verify(mEmergencyStateTracker)
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(selectedDomain,
dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
assertTrue(dialArgs.isEmergency);
assertEquals(eccCategory, dialArgs.eccCategory);
}
@Test
public void testOnSelectionTerminatedPerm() throws Exception {
setupForCallTest();
doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
.getDomainSelectionConnection(any(), anyInt(), eq(true));
doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
doReturn(mImsPhone).when(mPhone0).getImsPhone();
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
ArgumentCaptor.forClass(
DomainSelectionConnection.DomainSelectionConnectionCallback.class);
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
any(), callbackCaptor.capture());
DomainSelectionConnection.DomainSelectionConnectionCallback callback =
callbackCaptor.getValue();
assertNotNull(callback);
EmergencyCallDomainSelectionConnection ecdsc =
Mockito.mock(EmergencyCallDomainSelectionConnection.class);
doReturn(ecdsc).when(mDomainSelectionResolver)
.getDomainSelectionConnection(any(), anyInt(), eq(true));
callback.onSelectionTerminated(EMERGENCY_PERM_FAILURE);
ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
ArgumentCaptor.forClass(
DomainSelectionService.SelectionAttributes.class);
verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
}
@Test
public void testOnSelectionTerminatedTemp() throws Exception {
setupForCallTest();
doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
.getDomainSelectionConnection(any(), anyInt(), eq(true));
doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
doReturn(mImsPhone).when(mPhone0).getImsPhone();
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
ArgumentCaptor.forClass(
DomainSelectionConnection.DomainSelectionConnectionCallback.class);
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
any(), callbackCaptor.capture());
DomainSelectionConnection.DomainSelectionConnectionCallback callback =
callbackCaptor.getValue();
assertNotNull(callback);
EmergencyCallDomainSelectionConnection ecdsc =
Mockito.mock(EmergencyCallDomainSelectionConnection.class);
doReturn(ecdsc).when(mDomainSelectionResolver)
.getDomainSelectionConnection(any(), anyInt(), eq(true));
callback.onSelectionTerminated(EMERGENCY_TEMP_FAILURE);
ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
ArgumentCaptor.forClass(
DomainSelectionService.SelectionAttributes.class);
verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
}
@Test
public void testOnSelectionTerminatedUnspecified() throws Exception {
setupForCallTest();
doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
.getDomainSelectionConnection(any(), anyInt(), eq(true));
doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
doReturn(mImsPhone).when(mPhone0).getImsPhone();
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
ArgumentCaptor.forClass(
DomainSelectionConnection.DomainSelectionConnectionCallback.class);
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
any(), callbackCaptor.capture());
DomainSelectionConnection.DomainSelectionConnectionCallback callback =
callbackCaptor.getValue();
assertNotNull(callback);
callback.onSelectionTerminated(ERROR_UNSPECIFIED);
verify(mEmergencyCallDomainSelectionConnection).cancelSelection();
verify(mEmergencyStateTracker).endCall(eq(TELECOM_CALL_ID1));
}
@Test
public void testDomainSelectionLocalHangupStartEmergencyCall() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_CS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
CompletableFuture<Integer> future = new CompletableFuture<>();
doReturn(future).when(mEmergencyStateTracker)
.startEmergencyCall(any(), anyString(), eq(false));
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
verify(mEmergencyStateTracker)
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
TelephonyConnection c = new TestTelephonyConnection();
c.setTelecomCallId(TELECOM_CALL_ID1);
// dialing is canceled
mTestConnectionService.onLocalHangup(c);
// startEmergencyCall has completed
future.complete(NOT_DISCONNECTED);
// verify that createEmergencyConnection is discarded
verify(mEmergencyCallDomainSelectionConnection, times(0))
.createEmergencyConnection(any(), any());
}
@Test
public void testDomainSelectionLocalHangupCreateEmergencyConnection() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_CS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
CompletableFuture<Integer> future = new CompletableFuture<>();
doReturn(future).when(mEmergencyCallDomainSelectionConnection)
.createEmergencyConnection(any(), any());
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
TelephonyConnection c = new TestTelephonyConnection();
c.setTelecomCallId(TELECOM_CALL_ID1);
// dialing is canceled
mTestConnectionService.onLocalHangup(c);
// domain selection has completed
future.complete(selectedDomain);
// verify that dialing is discarded
verify(mPhone0, times(0)).dial(anyString(), any(), any());
}
@Test
public void testDomainSelectionRedialLocalHangupReselectDomain() throws Exception {
setupForCallTest();
int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
int selectedDomain = DOMAIN_CS;
TestTelephonyConnection c = setupForReDialForDomainSelection(
mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
c.setTelecomCallId(TELECOM_CALL_ID1);
CompletableFuture<Integer> future = new CompletableFuture<>();
doReturn(future).when(mEmergencyCallDomainSelectionConnection)
.reselectDomain(any());
assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
// dialing is canceled
mTestConnectionService.onLocalHangup(c);
// domain selection has completed
future.complete(selectedDomain);
// verify that dialing is discarded
verify(mPhone0, times(0)).dial(anyString(), any(), any());
}
@Test
public void testDomainSelectionNormalToEmergencyLocalHangupStartEmergencyCall()
throws Exception {
setupForCallTest();
int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
int selectedDomain = DOMAIN_CS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
TestTelephonyConnection c = setupForReDialForDomainSelection(
mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
c.setEmergencyServiceCategory(eccCategory);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
c.setTelecomCallId(TELECOM_CALL_ID1);
CompletableFuture<Integer> future = new CompletableFuture<>();
doReturn(future).when(mEmergencyStateTracker)
.startEmergencyCall(any(), anyString(), eq(false));
ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
assertTrue(mTestConnectionService.maybeReselectDomain(c,
preciseDisconnectCause, reasonInfo));
verify(mEmergencyStateTracker)
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
// dialing is canceled
mTestConnectionService.onLocalHangup(c);
// startEmergencyCall has completed
future.complete(NOT_DISCONNECTED);
// verify that createEmergencyConnection is discarded
verify(mEmergencyCallDomainSelectionConnection, times(0))
.createEmergencyConnection(any(), any());
}
@Test
public void testDomainSelectionNormalToEmergencyLocalHangupCreateEmergencyConnection()
throws Exception {
setupForCallTest();
int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
int eccCategory = EMERGENCY_SERVICE_CATEGORY_POLICE;
int selectedDomain = DOMAIN_CS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
TestTelephonyConnection c = setupForReDialForDomainSelection(
mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
c.setEmergencyServiceCategory(eccCategory);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
c.setTelecomCallId(TELECOM_CALL_ID1);
CompletableFuture<Integer> future = new CompletableFuture<>();
doReturn(future).when(mEmergencyCallDomainSelectionConnection)
.createEmergencyConnection(any(), any());
ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
assertTrue(mTestConnectionService.maybeReselectDomain(c,
preciseDisconnectCause, reasonInfo));
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
// dialing is canceled
mTestConnectionService.onLocalHangup(c);
// domain selection has completed
future.complete(selectedDomain);
// verify that dialing is discarded
verify(mPhone0, times(0)).dial(anyString(), any(), any());
}
@Test
public void testDomainSelectionListenOriginalConnectionConfigChange() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_PS;
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
verify(mEmergencyStateTracker)
.startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
verify(mPhone0).dial(anyString(), any(), any());
TestTelephonyConnection c = new TestTelephonyConnection();
c.setTelecomCallId(TELECOM_CALL_ID1);
c.setIsImsConnection(true);
Connection orgConn = c.getOriginalConnection();
doReturn(PhoneConstants.PHONE_TYPE_IMS).when(orgConn).getPhoneType();
TelephonyConnection.TelephonyConnectionListener connectionListener =
mTestConnectionService.getEmergencyConnectionListener();
connectionListener.onOriginalConnectionConfigured(c);
verify(mEmergencyStateTracker, times(1)).onEmergencyCallDomainUpdated(
eq(PhoneConstants.PHONE_TYPE_IMS), eq(TELECOM_CALL_ID1));
verify(mEmergencyStateTracker, times(0)).onEmergencyCallStateChanged(
any(), eq(TELECOM_CALL_ID1));
c.setActive();
doReturn(Call.State.ACTIVE).when(orgConn).getState();
connectionListener.onStateChanged(c, c.getState());
// ACTIVE sate is notified
verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
eq(Call.State.ACTIVE), eq(TELECOM_CALL_ID1));
// state change to HOLDING
c.setOnHold();
doReturn(Call.State.HOLDING).when(orgConn).getState();
connectionListener.onStateChanged(c, c.getState());
// state change not notified any more after CONNECTED once
verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
any(), eq(TELECOM_CALL_ID1));
// state change to ACTIVE again
c.setActive();
doReturn(Call.State.ACTIVE).when(orgConn).getState();
connectionListener.onStateChanged(c, c.getState());
// state change not notified any more after CONNECTED once
verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
any(), eq(TELECOM_CALL_ID1));
// SRVCC happens
c.setIsImsConnection(false);
orgConn = c.getOriginalConnection();
doReturn(PhoneConstants.PHONE_TYPE_GSM).when(orgConn).getPhoneType();
connectionListener.onOriginalConnectionConfigured(c);
// domain change notified
verify(mEmergencyStateTracker, times(1)).onEmergencyCallDomainUpdated(
eq(PhoneConstants.PHONE_TYPE_GSM), eq(TELECOM_CALL_ID1));
// state change to DISCONNECTED
c.setDisconnected(null);
doReturn(Call.State.DISCONNECTED).when(orgConn).getState();
connectionListener.onStateChanged(c, c.getState());
// state change not notified
verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
any(), eq(TELECOM_CALL_ID1));
}
@Test
public void testDomainSelectionTempFailure() throws Exception {
setupForCallTest();
int preciseDisconnectCause =
com.android.internal.telephony.CallFailCause.EMERGENCY_TEMP_FAILURE;
int disconnectCause = android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
int selectedDomain = DOMAIN_CS;
TestTelephonyConnection c = setupForReDialForDomainSelection(
mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
doReturn(new CompletableFuture()).when(mEmergencyCallDomainSelectionConnection)
.reselectDomain(any());
assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
}
@Test
public void testDomainSelectionPermFailure() throws Exception {
setupForCallTest();
int preciseDisconnectCause =
com.android.internal.telephony.CallFailCause.EMERGENCY_PERM_FAILURE;
int disconnectCause = android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
int selectedDomain = DOMAIN_CS;
TestTelephonyConnection c = setupForReDialForDomainSelection(
mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
doReturn(new CompletableFuture()).when(mEmergencyCallDomainSelectionConnection)
.reselectDomain(any());
assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
}
@Test
public void testDomainSelectionWithMmiCode() {
//UT domain selection should not be handled by new domain selector.
doNothing().when(mContext).startActivity(any());
setupForCallTest();
setupForDialForDomainSelection(mPhone0, 0, false);
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "*%2321%23", TELECOM_CALL_ID1));
verifyZeroInteractions(mNormalCallDomainSelectionConnection);
}
@Test
public void testNormalCallPsDomainSelection() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_PS;
setupForDialForDomainSelection(mPhone0, selectedDomain, false);
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(
selectedDomain, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
@Test
public void testNormalCallCsDomainSelection() throws Exception {
setupForCallTest();
int selectedDomain = DOMAIN_CS;
setupForDialForDomainSelection(mPhone0, selectedDomain, false);
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
DialArgs dialArgs = argsCaptor.getValue();
assertNotNull("DialArgs param is null", dialArgs);
assertNotNull("intentExtras is null", dialArgs.intentExtras);
assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
assertEquals(
selectedDomain, dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
}
private void setupForDialForDomainSelection(Phone mockPhone, int domain, boolean isEmergency) {
if (isEmergency) {
doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
.getDomainSelectionConnection(any(), anyInt(), eq(true));
doReturn(CompletableFuture.completedFuture(domain))
.when(mEmergencyCallDomainSelectionConnection)
.createEmergencyConnection(any(), any());
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
} else {
doReturn(mNormalCallDomainSelectionConnection).when(mDomainSelectionResolver)
.getDomainSelectionConnection(any(), eq(SELECTOR_TYPE_CALLING), eq(false));
doReturn(CompletableFuture.completedFuture(domain))
.when(mNormalCallDomainSelectionConnection)
.createNormalConnection(any(), any());
doReturn(false).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
}
doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
doReturn(mImsPhone).when(mockPhone).getImsPhone();
}
private TestTelephonyConnection setupForReDialForDomainSelection(
Phone mockPhone, int domain, int preciseDisconnectCause,
int disconnectCause, boolean fromEmergency) throws Exception {
try {
if (fromEmergency) {
doReturn(CompletableFuture.completedFuture(domain))
.when(mEmergencyCallDomainSelectionConnection)
.reselectDomain(any());
replaceInstance(TelephonyConnectionService.class,
"mEmergencyCallDomainSelectionConnection",
mTestConnectionService, mEmergencyCallDomainSelectionConnection);
replaceInstance(TelephonyConnectionService.class, "mEmergencyCallId",
mTestConnectionService, TELECOM_CALL_ID1);
} else {
doReturn(CompletableFuture.completedFuture(domain))
.when(mNormalCallDomainSelectionConnection).reselectDomain(any());
replaceInstance(TelephonyConnectionService.class, "mDomainSelectionConnection",
mTestConnectionService, mNormalCallDomainSelectionConnection);
}
} catch (Exception e) {
// This shouldn't happen
fail();
}
doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
TestTelephonyConnection c = new TestTelephonyConnection();
c.setTelecomCallId(TELECOM_CALL_ID1);
c.setMockPhone(mockPhone);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
Connection oc = c.getOriginalConnection();
doReturn(disconnectCause).when(oc).getDisconnectCause();
doReturn(preciseDisconnectCause).when(oc).getPreciseDisconnectCause();
return c;
}
private SimpleTelephonyConnection createTestConnection(PhoneAccountHandle handle,
int properties, boolean isEmergency) {
SimpleTelephonyConnection connection = new SimpleTelephonyConnection();
connection.setShouldTreatAsEmergencyCall(isEmergency);
connection.setConnectionProperties(properties);
connection.setPhoneAccountHandle(handle);
return connection;
}
private SimpleConference createTestConference(PhoneAccountHandle handle, int properties) {
SimpleConference conference = new SimpleConference(handle);
conference.setConnectionProperties(properties);
return conference;
}
/**
* Setup the mess of mocks for {@link #testSecondCallSameSubWontDisconnect()} and
* {@link #testIncomingDoesntRequestDisconnect()}.
*/
private void setupForCallTest() {
// Setup a bunch of stuff. Blech.
mTestConnectionService.setReadyForTest();
mPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
when(mCall.getState()).thenReturn(Call.State.INCOMING);
when(mCall.getPhone()).thenReturn(mPhone0);
when(mPhone0.getRingingCall()).thenReturn(mCall);
mPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
when(mCall2.getPhone()).thenReturn(mPhone1);
List<Phone> phones = new ArrayList<>(2);
doReturn(true).when(mPhone0).isRadioOn();
doReturn(true).when(mPhone1).isRadioOn();
doReturn(GSM_PHONE).when(mPhone0).getPhoneType();
doReturn(GSM_PHONE).when(mPhone1).getPhoneType();
phones.add(mPhone0);
phones.add(mPhone1);
setPhones(phones);
when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(eq(PHONE_ACCOUNT_HANDLE_1)))
.thenReturn(0);
when(mSubscriptionManagerProxy.getPhoneId(0)).thenReturn(0);
when(mPhoneFactoryProxy.getPhone(eq(0))).thenReturn(mPhone0);
when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(eq(PHONE_ACCOUNT_HANDLE_2)))
.thenReturn(1);
when(mSubscriptionManagerProxy.getPhoneId(1)).thenReturn(1);
when(mPhoneFactoryProxy.getPhone(eq(1))).thenReturn(mPhone1);
setupDeviceConfig(mPhone0, mPhone1, 1);
when(mInternalConnection.getCall()).thenReturn(mCall);
when(mInternalConnection.getState()).thenReturn(Call.State.ACTIVE);
when(mInternalConnection2.getCall()).thenReturn(mCall2);
when(mInternalConnection2.getState()).thenReturn(Call.State.WAITING);
}
/**
* Set up a mock MSIM device with TEST_ADDRESS set as an emergency number.
* @param isRoaming whether it is roaming
* @param setOperatorName whether operator name needs to set
* @param operatorNameLongName the operator long name if needs to set
* @param operatorNameShortName the operator short name if needs to set
* @param operatorNameNumeric the operator numeric name if needs to set
* @return the Phone associated with slot 0.
*/
private Phone setupConnectionServiceForDelayDial(boolean isRoaming, boolean setOperatorName,
String operatorNameLongName, String operatorNameShortName,
String operatorNameNumeric) {
ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
.setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
.setAddress(TEST_ADDRESS)
.build();
Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
false /*isEmergencyOnly*/);
Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
List<Phone> phones = new ArrayList<>(2);
doReturn(true).when(testPhone0).isRadioOn();
doReturn(true).when(testPhone1).isRadioOn();
phones.add(testPhone0);
phones.add(testPhone1);
setPhones(phones);
setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
setupDeviceConfig(testPhone0, testPhone1, 1);
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
TEST_ADDRESS.getSchemeSpecificPart());
HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
List<EmergencyNumber> numbers = new ArrayList<>();
numbers.add(setupEmergencyNumber(TEST_ADDRESS));
emergencyNumbers.put(0 /*subId*/, numbers);
doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
testPhone0.getServiceState().setRoaming(isRoaming);
if (setOperatorName) {
testPhone0.getServiceState().setOperatorName(operatorNameLongName,
operatorNameShortName, operatorNameNumeric);
}
mConnection = mTestConnectionService.onCreateOutgoingConnection(
PHONE_ACCOUNT_HANDLE_1, connectionRequest);
assertNotNull("test connection was not set up correctly.", mConnection);
return testPhone0;
}
/**
* Set up a mock MSIM device with TEST_ADDRESS set as an emergency number in airplane mode.
* @return the Phone associated with slot 0.
*/
private Phone setupConnectionServiceInApm() {
ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
.setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
.setAddress(TEST_ADDRESS)
.build();
Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_POWER_OFF,
false /*isEmergencyOnly*/);
Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_POWER_OFF,
false /*isEmergencyOnly*/);
doReturn(GSM_PHONE).when(testPhone0).getPhoneType();
doReturn(GSM_PHONE).when(testPhone1).getPhoneType();
List<Phone> phones = new ArrayList<>(2);
doReturn(false).when(testPhone0).isRadioOn();
doReturn(false).when(testPhone1).isRadioOn();
phones.add(testPhone0);
phones.add(testPhone1);
setPhones(phones);
setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
setupDeviceConfig(testPhone0, testPhone1, 0);
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
TEST_ADDRESS.getSchemeSpecificPart());
HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
List<EmergencyNumber> numbers = new ArrayList<>();
numbers.add(setupEmergencyNumber(TEST_ADDRESS));
emergencyNumbers.put(0 /*subId*/, numbers);
doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
mConnection = mTestConnectionService.onCreateOutgoingConnection(
PHONE_ACCOUNT_HANDLE_1, connectionRequest);
assertNotNull("test connection was not set up correctly.", mConnection);
return testPhone0;
}
private EmergencyNumber setupEmergencyNumber(Uri address) {
return new EmergencyNumber(address.getSchemeSpecificPart(), "", "",
EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
Collections.emptyList(),
EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
}
private void setupHandleToPhoneMap(PhoneAccountHandle handle, Phone phone) {
// use subId 0
when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(eq(handle))).thenReturn(0);
when(mSubscriptionManagerProxy.getPhoneId(eq(0))).thenReturn(0);
when(mPhoneFactoryProxy.getPhone(eq(0))).thenReturn(phone);
}
private AsyncResult getSuppServiceNotification(int notificationType, int code) {
SuppServiceNotification notification = new SuppServiceNotification();
notification.notificationType = notificationType;
notification.code = code;
return new AsyncResult(null, notification, null);
}
private Phone makeTestPhone(int phoneId, int serviceState, boolean isEmergencyOnly) {
Phone phone = mock(Phone.class);
ServiceState testServiceState = new ServiceState();
testServiceState.setState(serviceState);
testServiceState.setEmergencyOnly(isEmergencyOnly);
when(phone.getContext()).thenReturn(mContext);
when(phone.getServiceState()).thenReturn(testServiceState);
when(phone.getPhoneId()).thenReturn(phoneId);
when(phone.getDefaultPhone()).thenReturn(phone);
when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
when(phone.getServiceStateTracker()).thenReturn(mSST);
doNothing().when(phone).registerForPreciseCallStateChanged(any(Handler.class), anyInt(),
any(Object.class));
when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
return phone;
}
// Setup 2 SIM device
private void setupDeviceConfig(Phone slot0Phone, Phone slot1Phone, int defaultVoicePhoneId) {
when(mTelephonyManagerProxy.getPhoneCount()).thenReturn(2);
when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(false);
when(mSubscriptionManagerProxy.getDefaultVoicePhoneId()).thenReturn(defaultVoicePhoneId);
when(mPhoneFactoryProxy.getPhone(eq(SLOT_0_PHONE_ID))).thenReturn(slot0Phone);
when(mPhoneFactoryProxy.getPhone(eq(SLOT_1_PHONE_ID))).thenReturn(slot1Phone);
}
private void setDefaultDataPhoneId(int defaultDataPhoneId) {
when(mSubscriptionManagerProxy.getDefaultDataPhoneId()).thenReturn(defaultDataPhoneId);
}
private void setPhoneRadioAccessFamily(Phone phone, int radioAccessFamily) {
when(phone.getRadioAccessFamily()).thenReturn(radioAccessFamily);
}
private void setEmergencySmsMode(Phone phone, boolean isInEmergencySmsMode) {
when(phone.isInEmergencySmsMode()).thenReturn(isInEmergencySmsMode);
}
private void setPhoneSlotState(int slotId, int slotState) {
when(mSubscriptionManagerProxy.getSimStateForSlotIdx(slotId)).thenReturn(slotState);
}
private void setDefaultPhone(Phone phone) {
when(mPhoneFactoryProxy.getDefaultPhone()).thenReturn(phone);
}
private void setPhones(List<Phone> phones) {
when(mPhoneFactoryProxy.getPhones()).thenReturn(phones.toArray(new Phone[phones.size()]));
when(mPhoneFactoryProxy.getDefaultPhone()).thenReturn(phones.get(0));
}
private void setPhonesDialConnection(Phone phone, Connection c) {
try {
when(phone.dial(anyString(), any(), any())).thenReturn(c);
} catch (CallStateException e) {
// this shouldn't happen
fail();
}
}
private ConnectionRequest createConnectionRequest(
PhoneAccountHandle accountHandle, String address, String callId) {
return new ConnectionRequest.Builder()
.setAccountHandle(accountHandle)
.setAddress(Uri.parse("tel:" + address))
.setExtras(new Bundle())
.setTelecomCallId(callId)
.build();
}
private Executor getExecutor() {
return Runnable::run;
}
}