blob: 57713269c5968fd534e4ec31dac3a20590409393 [file] [log] [blame]
/*
* Copyright (C) 2019 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.internal.telephony;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
import static android.telephony.TelephonyManager.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
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 android.content.Intent;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.telephony.PhoneCapability;
import android.telephony.SubscriptionManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PhoneConfigurationManagerTest extends TelephonyTest {
// Mocked classes
Handler mHandler;
CommandsInterface mMockCi0;
CommandsInterface mMockCi1;
private Phone mPhone1; // mPhone as phone 0 is already defined in TelephonyTest.
PhoneConfigurationManager.MockableInterface mMi;
private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 1;
PhoneConfigurationManager mPcm;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
mHandler = mock(Handler.class);
mMockCi0 = mock(CommandsInterface.class);
mMockCi1 = mock(CommandsInterface.class);
mPhone1 = mock(Phone.class);
mMi = mock(PhoneConfigurationManager.MockableInterface.class);
mPhone.mCi = mMockCi0;
mCT.mCi = mMockCi0;
mPhone1.mCi = mMockCi1;
}
@After
public void tearDown() throws Exception {
mPcm = null;
super.tearDown();
}
private void setRebootRequiredForConfigSwitch(boolean rebootRequired) {
doReturn(rebootRequired).when(mMi).isRebootRequiredForModemConfigChange();
}
private void init(int numOfSim) throws Exception {
doReturn(numOfSim).when(mTelephonyManager).getActiveModemCount();
replaceInstance(PhoneConfigurationManager.class, "sInstance", null, null);
mPcm = PhoneConfigurationManager.init(mContext);
replaceInstance(PhoneConfigurationManager.class, "mMi", mPcm, mMi);
processAllMessages();
}
/**
* Test that a single phone case results in our phone being active and the RIL called
*/
@Test
@SmallTest
public void testGetPhoneCount() throws Exception {
init(1);
doReturn(1).when(mTelephonyManager).getActiveModemCount();
assertEquals(1, mPcm.getPhoneCount());
doReturn(2).when(mTelephonyManager).getActiveModemCount();
assertEquals(2, mPcm.getPhoneCount());
}
@Test
@SmallTest
public void testEnablePhone() throws Exception {
init(1);
// Phone is null. No crash.
mPcm.enablePhone(null, true, null);
Message message = new Message();
mPcm.enablePhone(mPhone, false, message);
verify(mMockCi0).enableModem(eq(false), eq(message));
}
@Test
@SmallTest
public void testGetDsdsCapability() throws Exception {
init(1);
assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
verify(mMockRadioConfig).getPhoneCapability(captor.capture());
Message msg = captor.getValue();
AsyncResult.forMessage(msg, PhoneCapability.DEFAULT_DSDS_CAPABILITY, null);
msg.sendToTarget();
processAllMessages();
// Not static capability should indicate DSDS capable.
assertEquals(PhoneCapability.DEFAULT_DSDS_CAPABILITY, mPcm.getStaticPhoneCapability());
}
@Test
@SmallTest
public void testSwitchMultiSimConfig_notDsdsCapable_shouldFail() throws Exception {
init(1);
assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
// Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported.
mPcm.switchMultiSimConfig(2);
verify(mMockRadioConfig, never()).setNumOfLiveModems(anyInt(), any());
}
@Test
@SmallTest
public void testSwitchMultiSimConfig_dsdsCapable_noRebootRequired() throws Exception {
init(1);
testSwitchFromSingleToDualSimModeNoReboot();
}
@Test
@SmallTest
public void testSwitchMultiSimConfig_multiSimToSingleSim() throws Exception {
mPhones = new Phone[]{mPhone, mPhone1};
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
init(2);
verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any());
verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
// Register for multi SIM config change.
mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
// Switch to single sim.
setRebootRequiredForConfigSwitch(false);
mPcm.switchMultiSimConfig(1);
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
verify(mMockRadioConfig).setNumOfLiveModems(eq(1), captor.capture());
// Send message back to indicate switch success.
Message message = captor.getValue();
AsyncResult.forMessage(message, null, null);
message.sendToTarget();
processAllMessages();
// Verify set system property being called.
verify(mMi).setMultiSimProperties(1);
verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(1));
// Capture and verify registration notification.
verify(mHandler).sendMessageAtTime(captor.capture(), anyLong());
message = captor.getValue();
assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what);
assertEquals(1, ((AsyncResult) message.obj).result);
// Capture and verify broadcast.
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).sendBroadcast(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertEquals(ACTION_MULTI_SIM_CONFIG_CHANGED, intent.getAction());
assertEquals(1, intent.getIntExtra(
EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0));
// Verify clearSubInfoRecord() and onSlotActiveStatusChange() are called for second phone,
// and not for the first one
verify(mSubscriptionController).clearSubInfoRecord(1);
verify(mMockCi1).onSlotActiveStatusChange(anyBoolean());
verify(mSubscriptionController, never()).clearSubInfoRecord(0);
verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean());
// Verify onPhoneRemoved() gets called on MultiSimSettingController phone
verify(mMultiSimSettingController).onPhoneRemoved();
}
@Test
@SmallTest
public void testNoCallPreferenceIsSetAfterSwitchToDsdsMode() throws Exception {
final int startingDefaultSubscriptionId = 2; // arbitrary value (can't be -1 which
// represents the "No Call Preference" value)
/*
TL;DR: the following mockito code block dynamically changes the last call to the getter
doAnswer(invocation -> {
Integer value = (Integer) invocation.getArguments()[0];
Mockito.when(object.getter()).thenReturn(value);
return null;
}).when(object).setter(anyInt());
read the code block as, whenever we call the setter, change the Mock call
of the getter to whatever argument value we last passed to the setter.
ex.) object.set( 2 ) --> next call to object.get() will return 2
*/
// setup mocks for VOICE mSubscriptionController. getter/setter
doAnswer(invocation -> {
Integer value = (Integer) invocation.getArguments()[0];
Mockito.when(mSubscriptionController.getDefaultVoiceSubId()).thenReturn(value);
return null;
}).when(mSubscriptionController).setDefaultVoiceSubId(anyInt());
// start off the phone stat with 1 active sim. reset values for new test.
init(1);
mSubscriptionController.setDefaultVoiceSubId(startingDefaultSubscriptionId);
// assert the mSubscriptionController registers the change
assertEquals(startingDefaultSubscriptionId, mSubscriptionController.getDefaultVoiceSubId());
// Perform the switch to DSDS mode and ensure all existing checks are not altered
testSwitchFromSingleToDualSimModeNoReboot();
// VOICE check
assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID /* No CALL Preference value */,
mSubscriptionController.getDefaultVoiceSubId()); // Now, when the user goes to
// place a CALL, they will be prompted on which sim to use.
}
/**
* must call init(1) from the parent test before calling this helper test
* @throws Exception
*/
public void testSwitchFromSingleToDualSimModeNoReboot() throws Exception {
verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any());
// Register for multi SIM config change.
mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
// Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported.
mPcm.switchMultiSimConfig(2);
verify(mMockRadioConfig, never()).setNumOfLiveModems(anyInt(), any());
// Send static capability back to indicate DSDS is supported.
clearInvocations(mMockRadioConfig);
testGetDsdsCapability();
// testGetDsdsCapability leads to another call to registerForAvailable()
verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any());
// Try to switch to DSDS.
setRebootRequiredForConfigSwitch(false);
mPhones = new Phone[]{mPhone, mPhone1};
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
mPcm.switchMultiSimConfig(2);
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
verify(mMockRadioConfig).setNumOfLiveModems(eq(2), captor.capture());
// Send message back to indicate switch success.
Message message = captor.getValue();
AsyncResult.forMessage(message, null, null);
message.sendToTarget();
processAllMessages();
// Verify set system property being called.
verify(mMi).setMultiSimProperties(2);
verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(2));
// Capture and verify registration notification.
verify(mHandler).sendMessageAtTime(captor.capture(), anyLong());
message = captor.getValue();
assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what);
assertEquals(2, ((AsyncResult) message.obj).result);
// Capture and verify broadcast.
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).sendBroadcast(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertEquals(ACTION_MULTI_SIM_CONFIG_CHANGED, intent.getAction());
assertEquals(2, intent.getIntExtra(
EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0));
// Verify registerForAvailable() and onSlotActiveStatusChange() are called for the second
// phone, and not for the first phone (registerForAvailable() was already called twice
// earlier so verify that the count is still at 2)
verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any());
verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean());
verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
verify(mMockCi1, times(1)).onSlotActiveStatusChange(anyBoolean());
}
}