| /* |
| * 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 junit.framework.Assert.assertEquals; |
| import static junit.framework.Assert.assertTrue; |
| import static junit.framework.Assert.fail; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.net.Uri; |
| import android.os.AsyncResult; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.telecom.DisconnectCause; |
| import android.telecom.TelecomManager; |
| import android.telephony.RadioAccessFamily; |
| import android.telephony.ServiceState; |
| import android.telephony.TelephonyManager; |
| import android.test.suitebuilder.annotation.SmallTest; |
| |
| import androidx.test.filters.FlakyTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.TelephonyTestBase; |
| import com.android.internal.telephony.CallStateException; |
| import com.android.internal.telephony.Connection; |
| import com.android.internal.telephony.Phone; |
| import com.android.internal.telephony.gsm.SuppServiceNotification; |
| |
| 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 java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Unit tests for TelephonyConnectionService. |
| */ |
| |
| @RunWith(AndroidJUnit4.class) |
| public class TelephonyConnectionServiceTest extends TelephonyTestBase { |
| |
| 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; |
| |
| @Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy; |
| @Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy; |
| @Mock TelephonyConnectionService.PhoneFactoryProxy mPhoneFactoryProxy; |
| |
| TelephonyConnectionService mTestConnectionService; |
| |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| mTestConnectionService = new TelephonyConnectionService(); |
| mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy); |
| mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy); |
| mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| mTestConnectionService = null; |
| super.tearDown(); |
| } |
| |
| /** |
| * 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 |
| * - 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, 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); |
| // Two SIMs inserted |
| setSlotHasIccCard(SLOT_0_PHONE_ID, true /*isInserted*/); |
| setSlotHasIccCard(SLOT_1_PHONE_ID, true /*isInserted*/); |
| |
| 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); |
| // Slot 0 has SIM inserted. |
| setSlotHasIccCard(SLOT_0_PHONE_ID, true /*isInserted*/); |
| setSlotHasIccCard(SLOT_1_PHONE_ID, false /*isInserted*/); |
| |
| 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); |
| // Slot 1 has SIM inserted. |
| setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/); |
| setSlotHasIccCard(SLOT_1_PHONE_ID, true /*isInserted*/); |
| |
| 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); |
| // No SIMs inserted |
| setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/); |
| setSlotHasIccCard(SLOT_1_PHONE_ID, false /*isInserted*/); |
| |
| 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); |
| // No SIMs inserted |
| setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/); |
| setSlotHasIccCard(SLOT_1_PHONE_ID, false /*isInserted*/); |
| |
| 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 |
| @FlakyTest |
| @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(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED); |
| |
| mTestConnectionService.retryOutgoingOriginalConnection(c, 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()); |
| } 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 |
| @FlakyTest |
| @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(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED); |
| |
| mTestConnectionService.retryOutgoingOriginalConnection(c, 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()); |
| } 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 |
| @FlakyTest |
| @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(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED); |
| List<Phone> phones = new ArrayList<>(2); |
| phones.add(slot0Phone); |
| phones.add(slot1Phone); |
| setPhones(phones); |
| |
| mTestConnectionService.retryOutgoingOriginalConnection(c, 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()); |
| } 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 |
| @FlakyTest |
| @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(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED); |
| List<Phone> phones = new ArrayList<>(2); |
| phones.add(slot0Phone); |
| phones.add(slot1Phone); |
| setPhones(phones); |
| |
| mTestConnectionService.retryOutgoingOriginalConnection(c, 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()); |
| } 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 |
| @FlakyTest |
| @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(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED); |
| List<Phone> phones = new ArrayList<>(2); |
| phones.add(slot0Phone); |
| phones.add(slot1Phone); |
| setPhones(phones); |
| |
| // First Temporary failure |
| mTestConnectionService.retryOutgoingOriginalConnection(c, 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, 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()); |
| verify(slot1Phone).dial(anyString(), 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 |
| @FlakyTest |
| @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(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED); |
| List<Phone> phones = new ArrayList<>(2); |
| phones.add(slot0Phone); |
| phones.add(slot1Phone); |
| setPhones(phones); |
| |
| // First Permanent failure |
| mTestConnectionService.retryOutgoingOriginalConnection(c, 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, 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()); |
| verify(slot0Phone, never()).dial(anyString(), 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()); |
| |
| // 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)); |
| } |
| |
| 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.getServiceState()).thenReturn(testServiceState); |
| when(phone.getPhoneId()).thenReturn(phoneId); |
| when(phone.getDefaultPhone()).thenReturn(phone); |
| return phone; |
| } |
| |
| // Setup 2 SIM device |
| private void setupDeviceConfig(Phone slot0Phone, Phone slot1Phone, int defaultVoicePhoneId) { |
| when(mTelephonyManagerProxy.getPhoneCount()).thenReturn(2); |
| 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 setPhoneRadioAccessFamily(Phone phone, int radioAccessFamily) { |
| when(phone.getRadioAccessFamily()).thenReturn(radioAccessFamily); |
| } |
| |
| private void setPhoneSlotState(int slotId, int slotState) { |
| when(mSubscriptionManagerProxy.getSimStateForSlotIdx(slotId)).thenReturn(slotState); |
| } |
| |
| private void setSlotHasIccCard(int slotId, boolean isInserted) { |
| when(mTelephonyManagerProxy.hasIccCard(slotId)).thenReturn(isInserted); |
| } |
| |
| 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()])); |
| } |
| |
| private void setPhonesDialConnection(Phone phone, Connection c) { |
| try { |
| when(phone.dial(anyString(), any())).thenReturn(c); |
| } catch (CallStateException e) { |
| // this shouldn't happen |
| fail(); |
| } |
| } |
| } |