1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony; 18 19 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED; 20 import static android.telephony.TelephonyManager.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyBoolean; 25 import static org.mockito.ArgumentMatchers.anyInt; 26 import static org.mockito.ArgumentMatchers.anyLong; 27 import static org.mockito.ArgumentMatchers.eq; 28 import static org.mockito.Mockito.clearInvocations; 29 import static org.mockito.Mockito.doAnswer; 30 import static org.mockito.Mockito.doReturn; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.times; 34 import static org.mockito.Mockito.verify; 35 36 import android.content.Intent; 37 import android.os.AsyncResult; 38 import android.os.Handler; 39 import android.os.Message; 40 import android.telephony.PhoneCapability; 41 import android.telephony.SubscriptionManager; 42 import android.test.suitebuilder.annotation.SmallTest; 43 import android.testing.AndroidTestingRunner; 44 import android.testing.TestableLooper; 45 46 import org.junit.After; 47 import org.junit.Before; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.mockito.ArgumentCaptor; 51 import org.mockito.Mockito; 52 53 @RunWith(AndroidTestingRunner.class) 54 @TestableLooper.RunWithLooper 55 public class PhoneConfigurationManagerTest extends TelephonyTest { 56 // Mocked classes 57 Handler mHandler; 58 CommandsInterface mMockCi0; 59 CommandsInterface mMockCi1; 60 private Phone mPhone1; // mPhone as phone 0 is already defined in TelephonyTest. 61 PhoneConfigurationManager.MockableInterface mMi; 62 63 private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 1; 64 PhoneConfigurationManager mPcm; 65 66 @Before setUp()67 public void setUp() throws Exception { 68 super.setUp(getClass().getSimpleName()); 69 mHandler = mock(Handler.class); 70 mMockCi0 = mock(CommandsInterface.class); 71 mMockCi1 = mock(CommandsInterface.class); 72 mPhone1 = mock(Phone.class); 73 mMi = mock(PhoneConfigurationManager.MockableInterface.class); 74 mPhone.mCi = mMockCi0; 75 mCT.mCi = mMockCi0; 76 mPhone1.mCi = mMockCi1; 77 } 78 79 @After tearDown()80 public void tearDown() throws Exception { 81 mPcm = null; 82 super.tearDown(); 83 } 84 setRebootRequiredForConfigSwitch(boolean rebootRequired)85 private void setRebootRequiredForConfigSwitch(boolean rebootRequired) { 86 doReturn(rebootRequired).when(mMi).isRebootRequiredForModemConfigChange(); 87 } 88 init(int numOfSim)89 private void init(int numOfSim) throws Exception { 90 doReturn(numOfSim).when(mTelephonyManager).getActiveModemCount(); 91 replaceInstance(PhoneConfigurationManager.class, "sInstance", null, null); 92 mPcm = PhoneConfigurationManager.init(mContext); 93 replaceInstance(PhoneConfigurationManager.class, "mMi", mPcm, mMi); 94 processAllMessages(); 95 } 96 97 /** 98 * Test that a single phone case results in our phone being active and the RIL called 99 */ 100 @Test 101 @SmallTest testGetPhoneCount()102 public void testGetPhoneCount() throws Exception { 103 init(1); 104 doReturn(1).when(mTelephonyManager).getActiveModemCount(); 105 assertEquals(1, mPcm.getPhoneCount()); 106 doReturn(2).when(mTelephonyManager).getActiveModemCount(); 107 assertEquals(2, mPcm.getPhoneCount()); 108 } 109 110 @Test 111 @SmallTest testEnablePhone()112 public void testEnablePhone() throws Exception { 113 init(1); 114 // Phone is null. No crash. 115 mPcm.enablePhone(null, true, null); 116 117 Message message = new Message(); 118 mPcm.enablePhone(mPhone, false, message); 119 verify(mMockCi0).enableModem(eq(false), eq(message)); 120 } 121 122 @Test 123 @SmallTest testGetDsdsCapability()124 public void testGetDsdsCapability() throws Exception { 125 init(1); 126 assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability()); 127 128 ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); 129 verify(mMockRadioConfig).getPhoneCapability(captor.capture()); 130 Message msg = captor.getValue(); 131 AsyncResult.forMessage(msg, PhoneCapability.DEFAULT_DSDS_CAPABILITY, null); 132 msg.sendToTarget(); 133 processAllMessages(); 134 135 // Not static capability should indicate DSDS capable. 136 assertEquals(PhoneCapability.DEFAULT_DSDS_CAPABILITY, mPcm.getStaticPhoneCapability()); 137 } 138 139 @Test 140 @SmallTest testSwitchMultiSimConfig_notDsdsCapable_shouldFail()141 public void testSwitchMultiSimConfig_notDsdsCapable_shouldFail() throws Exception { 142 init(1); 143 assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability()); 144 145 // Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported. 146 mPcm.switchMultiSimConfig(2); 147 verify(mMockRadioConfig, never()).setNumOfLiveModems(anyInt(), any()); 148 } 149 150 @Test 151 @SmallTest testSwitchMultiSimConfig_dsdsCapable_noRebootRequired()152 public void testSwitchMultiSimConfig_dsdsCapable_noRebootRequired() throws Exception { 153 init(1); 154 testSwitchFromSingleToDualSimModeNoReboot(); 155 } 156 157 @Test 158 @SmallTest testSwitchMultiSimConfig_multiSimToSingleSim()159 public void testSwitchMultiSimConfig_multiSimToSingleSim() throws Exception { 160 mPhones = new Phone[]{mPhone, mPhone1}; 161 replaceInstance(PhoneFactory.class, "sPhones", null, mPhones); 162 init(2); 163 verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any()); 164 verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any()); 165 166 // Register for multi SIM config change. 167 mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null); 168 verify(mHandler, never()).sendMessageAtTime(any(), anyLong()); 169 170 // Switch to single sim. 171 setRebootRequiredForConfigSwitch(false); 172 mPcm.switchMultiSimConfig(1); 173 ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); 174 verify(mMockRadioConfig).setNumOfLiveModems(eq(1), captor.capture()); 175 176 // Send message back to indicate switch success. 177 Message message = captor.getValue(); 178 AsyncResult.forMessage(message, null, null); 179 message.sendToTarget(); 180 processAllMessages(); 181 182 // Verify set system property being called. 183 verify(mMi).setMultiSimProperties(1); 184 verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(1)); 185 186 // Capture and verify registration notification. 187 verify(mHandler).sendMessageAtTime(captor.capture(), anyLong()); 188 message = captor.getValue(); 189 assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what); 190 assertEquals(1, ((AsyncResult) message.obj).result); 191 192 // Capture and verify broadcast. 193 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 194 verify(mContext).sendBroadcast(intentCaptor.capture()); 195 Intent intent = intentCaptor.getValue(); 196 assertEquals(ACTION_MULTI_SIM_CONFIG_CHANGED, intent.getAction()); 197 assertEquals(1, intent.getIntExtra( 198 EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0)); 199 200 // Verify clearSubInfoRecord() and onSlotActiveStatusChange() are called for second phone, 201 // and not for the first one 202 verify(mSubscriptionController).clearSubInfoRecord(1); 203 verify(mMockCi1).onSlotActiveStatusChange(anyBoolean()); 204 verify(mSubscriptionController, never()).clearSubInfoRecord(0); 205 verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean()); 206 207 // Verify onPhoneRemoved() gets called on MultiSimSettingController phone 208 verify(mMultiSimSettingController).onPhoneRemoved(); 209 } 210 211 @Test 212 @SmallTest testNoCallPreferenceIsSetAfterSwitchToDsdsMode()213 public void testNoCallPreferenceIsSetAfterSwitchToDsdsMode() throws Exception { 214 final int startingDefaultSubscriptionId = 2; // arbitrary value (can't be -1 which 215 // represents the "No Call Preference" value) 216 217 /* 218 TL;DR: the following mockito code block dynamically changes the last call to the getter 219 220 doAnswer(invocation -> { 221 Integer value = (Integer) invocation.getArguments()[0]; 222 Mockito.when(object.getter()).thenReturn(value); 223 return null; 224 }).when(object).setter(anyInt()); 225 226 read the code block as, whenever we call the setter, change the Mock call 227 of the getter to whatever argument value we last passed to the setter. 228 229 ex.) object.set( 2 ) --> next call to object.get() will return 2 230 */ 231 232 // setup mocks for VOICE mSubscriptionController. getter/setter 233 doAnswer(invocation -> { 234 Integer value = (Integer) invocation.getArguments()[0]; 235 Mockito.when(mSubscriptionController.getDefaultVoiceSubId()).thenReturn(value); 236 return null; 237 }).when(mSubscriptionController).setDefaultVoiceSubId(anyInt()); 238 239 // start off the phone stat with 1 active sim. reset values for new test. 240 init(1); 241 242 mSubscriptionController.setDefaultVoiceSubId(startingDefaultSubscriptionId); 243 244 // assert the mSubscriptionController registers the change 245 assertEquals(startingDefaultSubscriptionId, mSubscriptionController.getDefaultVoiceSubId()); 246 247 // Perform the switch to DSDS mode and ensure all existing checks are not altered 248 testSwitchFromSingleToDualSimModeNoReboot(); 249 250 // VOICE check 251 assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID /* No CALL Preference value */, 252 mSubscriptionController.getDefaultVoiceSubId()); // Now, when the user goes to 253 // place a CALL, they will be prompted on which sim to use. 254 } 255 256 /** 257 * must call init(1) from the parent test before calling this helper test 258 * @throws Exception 259 */ testSwitchFromSingleToDualSimModeNoReboot()260 public void testSwitchFromSingleToDualSimModeNoReboot() throws Exception { 261 verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any()); 262 263 // Register for multi SIM config change. 264 mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null); 265 verify(mHandler, never()).sendMessageAtTime(any(), anyLong()); 266 267 // Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported. 268 mPcm.switchMultiSimConfig(2); 269 verify(mMockRadioConfig, never()).setNumOfLiveModems(anyInt(), any()); 270 271 // Send static capability back to indicate DSDS is supported. 272 clearInvocations(mMockRadioConfig); 273 testGetDsdsCapability(); 274 // testGetDsdsCapability leads to another call to registerForAvailable() 275 verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any()); 276 277 // Try to switch to DSDS. 278 setRebootRequiredForConfigSwitch(false); 279 mPhones = new Phone[]{mPhone, mPhone1}; 280 replaceInstance(PhoneFactory.class, "sPhones", null, mPhones); 281 mPcm.switchMultiSimConfig(2); 282 ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); 283 verify(mMockRadioConfig).setNumOfLiveModems(eq(2), captor.capture()); 284 285 // Send message back to indicate switch success. 286 Message message = captor.getValue(); 287 AsyncResult.forMessage(message, null, null); 288 message.sendToTarget(); 289 processAllMessages(); 290 291 // Verify set system property being called. 292 verify(mMi).setMultiSimProperties(2); 293 verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(2)); 294 295 // Capture and verify registration notification. 296 verify(mHandler).sendMessageAtTime(captor.capture(), anyLong()); 297 message = captor.getValue(); 298 assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what); 299 assertEquals(2, ((AsyncResult) message.obj).result); 300 301 // Capture and verify broadcast. 302 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 303 verify(mContext).sendBroadcast(intentCaptor.capture()); 304 Intent intent = intentCaptor.getValue(); 305 assertEquals(ACTION_MULTI_SIM_CONFIG_CHANGED, intent.getAction()); 306 assertEquals(2, intent.getIntExtra( 307 EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0)); 308 309 // Verify registerForAvailable() and onSlotActiveStatusChange() are called for the second 310 // phone, and not for the first phone (registerForAvailable() was already called twice 311 // earlier so verify that the count is still at 2) 312 verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any()); 313 verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean()); 314 verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any()); 315 verify(mMockCi1, times(1)).onSlotActiveStatusChange(anyBoolean()); 316 } 317 } 318