• 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
testConfigureAndGetMaxActiveVoiceSubscriptions()141     public void testConfigureAndGetMaxActiveVoiceSubscriptions() throws Exception {
142         init(2);
143         assertEquals(1, mPcm.getStaticPhoneCapability().getMaxActiveVoiceSubscriptions());
144 
145         PhoneCapability dualActiveVoiceSubCapability = new PhoneCapability.Builder(
146                 PhoneCapability.DEFAULT_DSDS_CAPABILITY)
147                 .setMaxActiveVoiceSubscriptions(2)
148                 .build();
149 
150         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
151         verify(mMockRadioConfig).getPhoneCapability(captor.capture());
152         Message msg = captor.getValue();
153         AsyncResult.forMessage(msg, dualActiveVoiceSubCapability, null);
154         msg.sendToTarget();
155         processAllMessages();
156 
157         assertEquals(2, mPcm.getStaticPhoneCapability().getMaxActiveVoiceSubscriptions());
158     }
159 
160     @Test
161     @SmallTest
testSwitchMultiSimConfig_notDsdsCapable_shouldFail()162     public void testSwitchMultiSimConfig_notDsdsCapable_shouldFail() throws Exception {
163         init(1);
164         assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
165 
166         // Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported.
167         mPcm.switchMultiSimConfig(2);
168         verify(mMockRadioConfig, never()).setNumOfLiveModems(anyInt(), any());
169     }
170 
171     @Test
172     @SmallTest
testSwitchMultiSimConfig_dsdsCapable_noRebootRequired()173     public void testSwitchMultiSimConfig_dsdsCapable_noRebootRequired() throws Exception {
174         init(1);
175         testSwitchFromSingleToDualSimModeNoReboot();
176     }
177 
178     @Test
179     @SmallTest
testSwitchMultiSimConfig_multiSimToSingleSim()180     public void testSwitchMultiSimConfig_multiSimToSingleSim() throws Exception {
181         mPhones = new Phone[]{mPhone, mPhone1};
182         replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
183         init(2);
184         verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any());
185         verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
186 
187         // Register for multi SIM config change.
188         mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
189         verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
190 
191         // Switch to single sim.
192         setRebootRequiredForConfigSwitch(false);
193         mPcm.switchMultiSimConfig(1);
194         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
195         verify(mMockRadioConfig).setNumOfLiveModems(eq(1), captor.capture());
196 
197         // Send message back to indicate switch success.
198         Message message = captor.getValue();
199         AsyncResult.forMessage(message, null, null);
200         message.sendToTarget();
201         processAllMessages();
202 
203         // Verify set system property being called.
204         verify(mMi).setMultiSimProperties(1);
205         verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(1));
206 
207         // Capture and verify registration notification.
208         verify(mHandler).sendMessageAtTime(captor.capture(), anyLong());
209         message = captor.getValue();
210         assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what);
211         assertEquals(1, ((AsyncResult) message.obj).result);
212 
213         // Capture and verify broadcast.
214         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
215         verify(mContext).sendBroadcast(intentCaptor.capture());
216         Intent intent = intentCaptor.getValue();
217         assertEquals(ACTION_MULTI_SIM_CONFIG_CHANGED, intent.getAction());
218         assertEquals(1, intent.getIntExtra(
219                 EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0));
220 
221         // Verify clearSubInfoRecord() and onSlotActiveStatusChange() are called for second phone,
222         // and not for the first one
223         verify(mSubscriptionManagerService).markSubscriptionsInactive(1);
224         verify(mMockCi1).onSlotActiveStatusChange(anyBoolean());
225         verify(mSubscriptionManagerService, never()).markSubscriptionsInactive(0);
226         verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean());
227 
228         // Verify onPhoneRemoved() gets called on MultiSimSettingController phone
229         verify(mMultiSimSettingController).onPhoneRemoved();
230     }
231 
232     @Test
233     @SmallTest
testNoCallPreferenceIsSetAfterSwitchToDsdsMode()234     public void testNoCallPreferenceIsSetAfterSwitchToDsdsMode() throws Exception {
235         final int startingDefaultSubscriptionId = 2; // arbitrary value (can't be -1 which
236         // represents the "No Call Preference" value)
237 
238         /*
239             TL;DR:  the following mockito code block dynamically changes the last call to the getter
240 
241             doAnswer(invocation -> {
242                 Integer value = (Integer) invocation.getArguments()[0];
243                 Mockito.when(object.getter()).thenReturn(value);
244                 return null;
245             }).when(object).setter(anyInt());
246 
247             read the code block as, whenever we call the setter, change the Mock call
248             of the getter to whatever argument value we last passed to the setter.
249 
250             ex.)    object.set( 2 )   --> next call to object.get() will return 2
251          */
252 
253         // setup mocks for  VOICE mSubscriptionManagerService. getter/setter
254         doAnswer(invocation -> {
255             Integer value = (Integer) invocation.getArguments()[0];
256             Mockito.when(mSubscriptionManagerService.getDefaultVoiceSubId()).thenReturn(value);
257             return null;
258         }).when(mSubscriptionManagerService).setDefaultVoiceSubId(anyInt());
259 
260 
261         // start off the phone stat with 1 active sim. reset values for new test.
262         init(1);
263 
264         mSubscriptionManagerService.setDefaultVoiceSubId(startingDefaultSubscriptionId);
265         assertEquals(startingDefaultSubscriptionId,
266                 mSubscriptionManagerService.getDefaultVoiceSubId());
267 
268         // Perform the switch to DSDS mode and ensure all existing checks are not altered
269         testSwitchFromSingleToDualSimModeNoReboot();
270 
271         // VOICE check
272         assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID /* No CALL Preference value */,
273                 mSubscriptionManagerService.getDefaultVoiceSubId());
274         // Now, when the user goes to place a CALL, they will be prompted on which sim to use.
275     }
276 
277     /**
278      * must call init(1) from the parent test before calling this helper test
279      * @throws Exception
280      */
testSwitchFromSingleToDualSimModeNoReboot()281     public void testSwitchFromSingleToDualSimModeNoReboot() throws Exception {
282         verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any());
283 
284         // Register for multi SIM config change.
285         mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
286         verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
287 
288         // Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported.
289         mPcm.switchMultiSimConfig(2);
290         verify(mMockRadioConfig, never()).setNumOfLiveModems(anyInt(), any());
291 
292         // Send static capability back to indicate DSDS is supported.
293         clearInvocations(mMockRadioConfig);
294         testGetDsdsCapability();
295         // testGetDsdsCapability leads to another call to registerForAvailable()
296         verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any());
297 
298         // Try to switch to DSDS.
299         setRebootRequiredForConfigSwitch(false);
300         mPhones = new Phone[]{mPhone, mPhone1};
301         replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
302         mPcm.switchMultiSimConfig(2);
303         ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
304         verify(mMockRadioConfig).setNumOfLiveModems(eq(2), captor.capture());
305 
306         // Send message back to indicate switch success.
307         Message message = captor.getValue();
308         AsyncResult.forMessage(message, null, null);
309         message.sendToTarget();
310         processAllMessages();
311 
312         // Verify set system property being called.
313         verify(mMi).setMultiSimProperties(2);
314         verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(2));
315 
316         // Capture and verify registration notification.
317         verify(mHandler).sendMessageAtTime(captor.capture(), anyLong());
318         message = captor.getValue();
319         assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what);
320         assertEquals(2, ((AsyncResult) message.obj).result);
321 
322         // Capture and verify broadcast.
323         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
324         verify(mContext).sendBroadcast(intentCaptor.capture());
325         Intent intent = intentCaptor.getValue();
326         assertEquals(ACTION_MULTI_SIM_CONFIG_CHANGED, intent.getAction());
327         assertEquals(2, intent.getIntExtra(
328                 EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0));
329 
330         // Verify registerForAvailable() and onSlotActiveStatusChange() are called for the second
331         // phone, and not for the first phone (registerForAvailable() was already called twice
332         // earlier so verify that the count is still at 2)
333         verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any());
334         verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean());
335         verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
336         verify(mMockCi1, times(1)).onSlotActiveStatusChange(anyBoolean());
337     }
338 }
339