• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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