1/*
2 * Copyright 2017 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
17package com.android.internal.telephony.ims;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.fail;
21import static org.mockito.ArgumentMatchers.eq;
22import static org.mockito.Mockito.anyInt;
23import static org.mockito.Mockito.anyString;
24import static org.mockito.Mockito.doReturn;
25import static org.mockito.Mockito.timeout;
26import static org.mockito.Mockito.times;
27import static org.mockito.Mockito.verify;
28import static org.mockito.Mockito.when;
29
30import android.os.IBinder;
31import android.os.PersistableBundle;
32import android.telephony.CarrierConfigManager;
33import android.telephony.SubscriptionManager;
34import android.telephony.ims.stub.ImsConfigImplBase;
35import android.test.suitebuilder.annotation.SmallTest;
36
37import com.android.ims.ImsConfig;
38import com.android.ims.ImsManager;
39import com.android.ims.MmTelFeatureConnection;
40import com.android.internal.telephony.TelephonyTest;
41
42import org.junit.After;
43import org.junit.Before;
44import org.junit.Test;
45import org.mockito.Mock;
46
47import java.util.Hashtable;
48
49public class ImsManagerTest extends TelephonyTest {
50    private static final String UNSET_PROVISIONED_STRING = "unset";
51    private static final boolean ENHANCED_4G_MODE_DEFAULT_VAL = true;
52    private static final boolean ENHANCED_4G_MODE_EDITABLE = true;
53    private static final boolean WFC_IMS_ENABLE_DEFAULT_VAL = false;
54    private static final boolean WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL = true;
55    private static final boolean VT_IMS_ENABLE_DEFAULT_VAL = true;
56    private static final boolean WFC_IMS_EDITABLE_VAL = true;
57    private static final boolean WFC_IMS_NOT_EDITABLE_VAL = false;
58    private static final int WFC_IMS_MODE_DEFAULT_VAL =
59            ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
60    private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL =
61            ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED;
62
63    PersistableBundle mBundle;
64    @Mock IBinder mBinder;
65    @Mock ImsConfigImplBase mImsConfigImplBaseMock;
66    Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
67    Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
68    ImsConfigImplBase.ImsConfigStub mImsConfigStub;
69    @Mock MmTelFeatureConnection mMmTelFeatureConnection;
70
71    private final int[] mSubId = {0};
72    private int mPhoneId;
73
74    @Before
75    public void setUp() throws Exception {
76        super.setUp("SubscriptionControllerTest");
77        mPhoneId = mPhone.getPhoneId();
78        mBundle = mContextFixture.getCarrierConfigBundle();
79
80        doReturn(mSubId).when(mSubscriptionController).getSubId(mPhoneId);
81
82        doReturn(mSubscriptionController).when(mBinder).queryLocalInterface(anyString());
83        mServiceManagerMockedServices.put("isub", mBinder);
84
85        doReturn(true).when(mMmTelFeatureConnection).isBinderAlive();
86
87        mImsManagerInstances.remove(mPhoneId);
88
89        setDefaultValues();
90    }
91
92    @After
93    public void tearDown() throws Exception {
94        super.tearDown();
95    }
96
97    private void setDefaultValues() {
98        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
99                ENHANCED_4G_MODE_EDITABLE);
100        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
101                WFC_IMS_EDITABLE_VAL);
102        mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL,
103                WFC_IMS_ENABLE_DEFAULT_VAL);
104        mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL,
105                WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL);
106        mBundle.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT,
107                WFC_IMS_MODE_DEFAULT_VAL);
108        mBundle.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT,
109                WFC_IMS_ROAMING_MODE_DEFAULT_VAL);
110        mBundle.putBoolean(CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL,
111                ENHANCED_4G_MODE_DEFAULT_VAL);
112        mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, true);
113    }
114
115    @Test @SmallTest
116    public void testGetDefaultValues() {
117        doReturn("-1").when(mSubscriptionController)
118                .getSubscriptionProperty(anyInt(), anyString(), anyString());
119
120        ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
121
122        assertEquals(WFC_IMS_ENABLE_DEFAULT_VAL, imsManager.isWfcEnabledByUser());
123        verify(mSubscriptionController, times(1)).getSubscriptionProperty(
124                anyInt(),
125                eq(SubscriptionManager.WFC_IMS_ENABLED),
126                anyString());
127
128        assertEquals(ENHANCED_4G_MODE_DEFAULT_VAL,
129                imsManager.isEnhanced4gLteModeSettingEnabledByUser());
130        verify(mSubscriptionController, times(1)).getSubscriptionProperty(
131                anyInt(),
132                eq(SubscriptionManager.ENHANCED_4G_MODE_ENABLED),
133                anyString());
134
135        assertEquals(WFC_IMS_MODE_DEFAULT_VAL, imsManager.getWfcMode(false));
136        verify(mSubscriptionController, times(1)).getSubscriptionProperty(
137                anyInt(),
138                eq(SubscriptionManager.WFC_IMS_MODE),
139                anyString());
140
141        assertEquals(WFC_IMS_ROAMING_MODE_DEFAULT_VAL, imsManager.getWfcMode(true));
142        verify(mSubscriptionController, times(1)).getSubscriptionProperty(
143                anyInt(),
144                eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
145                anyString());
146
147        assertEquals(VT_IMS_ENABLE_DEFAULT_VAL, imsManager.isVtEnabledByUser());
148        verify(mSubscriptionController, times(1)).getSubscriptionProperty(
149                anyInt(),
150                eq(SubscriptionManager.VT_IMS_ENABLED),
151                anyString());
152    }
153
154    @Test @SmallTest
155    public void testSetValues() {
156        ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
157
158        imsManager.setWfcMode(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
159        verify(mSubscriptionController, times(1)).setSubscriptionProperty(
160                eq(mSubId[0]),
161                eq(SubscriptionManager.WFC_IMS_MODE),
162                eq("1"));
163
164        imsManager.setWfcMode(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED, true);
165        verify(mSubscriptionController, times(1)).setSubscriptionProperty(
166                eq(mSubId[0]),
167                eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
168                eq("1"));
169
170        imsManager.setVtSetting(false);
171        verify(mSubscriptionController, times(1)).setSubscriptionProperty(
172                eq(mSubId[0]),
173                eq(SubscriptionManager.VT_IMS_ENABLED),
174                eq("0"));
175
176        // enhanced 4g mode must be editable to use setEnhanced4gLteModeSetting
177        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
178                ENHANCED_4G_MODE_EDITABLE);
179        imsManager.setEnhanced4gLteModeSetting(true);
180        verify(mSubscriptionController, times(1)).setSubscriptionProperty(
181                eq(mSubId[0]),
182                eq(SubscriptionManager.ENHANCED_4G_MODE_ENABLED),
183                eq("1"));
184
185        imsManager.setWfcSetting(true);
186        verify(mSubscriptionController, times(1)).setSubscriptionProperty(
187                eq(mSubId[0]),
188                eq(SubscriptionManager.WFC_IMS_ENABLED),
189                eq("1"));
190    }
191    @Test
192    public void testGetProvisionedValues() throws Exception {
193        ImsManager imsManager = initializeProvisionedValues();
194
195        assertEquals(true, imsManager.isWfcProvisionedOnDevice());
196        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
197                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
198
199        assertEquals(true, imsManager.isVtProvisionedOnDevice());
200        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
201                eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
202
203        assertEquals(true, imsManager.isVolteProvisionedOnDevice());
204        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
205                eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
206
207        // If we call get again, times should still be one because the value should be fetched
208        // from cache.
209        assertEquals(true, imsManager.isWfcProvisionedOnDevice());
210        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
211                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
212
213        assertEquals(true, imsManager.isVtProvisionedOnDevice());
214        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
215                eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
216
217        assertEquals(true, imsManager.isVolteProvisionedOnDevice());
218        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
219                eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
220    }
221
222    @Test
223    public void testSetProvisionedValues() throws Exception {
224        ImsManager imsManager = initializeProvisionedValues();
225
226        assertEquals(true, imsManager.isWfcProvisionedOnDevice());
227        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
228                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
229
230        imsManager.getConfigInterface().setProvisionedValue(
231                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED,
232                ImsConfig.FeatureValueConstants.OFF);
233
234        assertEquals(0, (int) mProvisionedIntVals.get(
235                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
236
237        assertEquals(false, imsManager.isWfcProvisionedOnDevice());
238
239        verify(mImsConfigImplBaseMock, times(1)).setConfig(
240                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED),
241                eq(0));
242        verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
243                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
244
245    }
246
247    /**
248     * Tests that when a WFC mode is set for home/roaming, that setting is sent to the ImsService
249     * correctly.
250     *
251     * Preconditions:
252     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
253     */
254    @Test @SmallTest
255    public void testSetWfcSetting_true_shouldSetWfcModeWrtRoamingState() throws Exception {
256        // First, Set WFC home/roaming mode that is not the Carrier Config default.
257        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED))
258                .when(mSubscriptionController).getSubscriptionProperty(
259                        anyInt(),
260                        eq(SubscriptionManager.WFC_IMS_MODE),
261                        anyString());
262        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
263                .when(mSubscriptionController).getSubscriptionProperty(
264                        anyInt(),
265                        eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
266                        anyString());
267        ImsManager imsManager = initializeProvisionedValues();
268
269        // Roaming
270        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
271        // Turn on WFC
272        imsManager.setWfcSetting(true);
273        // Roaming mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
274        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
275                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
276                eq(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED));
277
278        // Not roaming
279        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
280        // Turn on WFC
281        imsManager.setWfcSetting(true);
282        // Home mode (WIFI_PREFERRED) should be set. With 1000 ms timeout.
283        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
284                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
285                eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED));
286    }
287
288    /**
289     * Tests that the settings for WFC mode are ignored if the Carrier sets the settings to not
290     * editable.
291     *
292     * Preconditions:
293     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = false
294     */
295    @Test @SmallTest
296    public void testSetWfcSetting_wfcNotEditable() throws Exception {
297        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
298                WFC_IMS_NOT_EDITABLE_VAL);
299        // Set some values that are different than the defaults for WFC mode.
300        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
301                .when(mSubscriptionController).getSubscriptionProperty(
302                anyInt(),
303                eq(SubscriptionManager.WFC_IMS_MODE),
304                anyString());
305        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
306                .when(mSubscriptionController).getSubscriptionProperty(
307                anyInt(),
308                eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
309                anyString());
310        ImsManager imsManager = initializeProvisionedValues();
311
312        // Roaming
313        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
314        // Turn on WFC
315        imsManager.setWfcSetting(true);
316        // User defined setting for Roaming mode (WIFI_ONLY) should be set independent of whether or
317        // not WFC mode is editable. With 1000 ms timeout.
318        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
319                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
320                eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
321
322        // Not roaming
323        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
324        // Turn on WFC
325        imsManager.setWfcSetting(true);
326        // Default Home mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
327        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
328                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
329                eq(WFC_IMS_MODE_DEFAULT_VAL));
330    }
331
332    /**
333     * Tests that the CarrierConfig defaults will be used if no setting is set in the Subscription
334     * Manager.
335     *
336     * Preconditions:
337     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
338     *  - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = Carrier preferred
339     *  - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = WiFi preferred
340     */
341    @Test @SmallTest
342    public void testSetWfcSetting_noUserSettingSet() throws Exception {
343        ImsManager imsManager = initializeProvisionedValues();
344
345        // Roaming
346        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
347        // Turn on WFC
348        imsManager.setWfcSetting(true);
349
350        // Default Roaming mode (WIFI_PREFERRED) for carrier should be set. With 1000 ms timeout.
351        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
352                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
353                eq(WFC_IMS_ROAMING_MODE_DEFAULT_VAL));
354
355        // Not roaming
356        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
357        // Turn on WFC
358        imsManager.setWfcSetting(true);
359
360        // Default Home mode (CELLULAR_PREFERRED) for carrier should be set. With 1000 ms timeout.
361        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
362                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
363                eq(WFC_IMS_MODE_DEFAULT_VAL));
364    }
365
366    private ImsManager initializeProvisionedValues() throws Exception {
367        when(mImsConfigImplBaseMock.getConfigInt(anyInt()))
368                .thenAnswer(invocation ->  {
369                    return getProvisionedInt((Integer) (invocation.getArguments()[0]));
370                });
371
372        when(mImsConfigImplBaseMock.setConfig(anyInt(), anyInt()))
373                .thenAnswer(invocation ->  {
374                    mProvisionedIntVals.put((Integer) (invocation.getArguments()[0]),
375                            (Integer) (invocation.getArguments()[1]));
376                    return ImsConfig.OperationStatusConstants.SUCCESS;
377                });
378
379
380        // Configure ImsConfigStub
381        mImsConfigStub = new ImsConfigImplBase.ImsConfigStub(mImsConfigImplBaseMock);
382        doReturn(mImsConfigStub).when(mMmTelFeatureConnection).getConfigInterface();
383
384        // Configure ImsManager
385        ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
386        try {
387            replaceInstance(ImsManager.class, "mMmTelFeatureConnection", imsManager,
388                    mMmTelFeatureConnection);
389        } catch (Exception ex) {
390            fail("failed with " + ex);
391        }
392
393        return imsManager;
394    }
395
396    // If the value is ever set, return the set value. If not, return a constant value 1000.
397    private int getProvisionedInt(int item) {
398        if (mProvisionedIntVals.containsKey(item)) {
399            return mProvisionedIntVals.get(item);
400        } else {
401            return ImsConfig.FeatureValueConstants.ON;
402        }
403    }
404
405    // If the value is ever set, return the set value. If not, return a constant value "unset".
406    private String getProvisionedString(int item) {
407        if (mProvisionedStringVals.containsKey(item)) {
408            return mProvisionedStringVals.get(item);
409        } else {
410            return UNSET_PROVISIONED_STRING;
411        }
412    }
413}
414