1 /*
2  * Copyright (C) 2021 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.phone;
18 
19 import static com.android.TestContext.STUB_PERMISSION_ENABLE_ALL;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assert.assertThrows;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.anyString;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.ArgumentMatchers.nullable;
29 import static org.mockito.Mockito.doNothing;
30 import static org.mockito.Mockito.doReturn;
31 import static org.mockito.Mockito.verify;
32 
33 import android.content.Intent;
34 import android.content.SharedPreferences;
35 import android.content.pm.PackageInfo;
36 import android.content.pm.PackageManager;
37 import android.content.res.Resources;
38 import android.os.Build;
39 import android.os.Handler;
40 import android.os.HandlerThread;
41 import android.os.Message;
42 import android.os.PersistableBundle;
43 import android.os.UserHandle;
44 import android.service.carrier.CarrierIdentifier;
45 import android.telephony.CarrierConfigManager;
46 import android.telephony.SubscriptionManager;
47 import android.telephony.TelephonyManager;
48 import android.testing.TestableLooper;
49 
50 import androidx.test.InstrumentationRegistry;
51 import androidx.test.ext.junit.runners.AndroidJUnit4;
52 
53 import com.android.TelephonyTestBase;
54 import com.android.internal.telephony.IccCardConstants;
55 import com.android.internal.telephony.SubscriptionInfoUpdater;
56 
57 import org.junit.After;
58 import org.junit.Before;
59 import org.junit.Ignore;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.Mock;
63 import org.mockito.Mockito;
64 
65 import java.io.FileDescriptor;
66 import java.io.PrintWriter;
67 import java.io.StringWriter;
68 import java.util.List;
69 
70 /**
71  * Unit Test for CarrierConfigLoader.
72  */
73 @RunWith(AndroidJUnit4.class)
74 public class CarrierConfigLoaderTest extends TelephonyTestBase {
75 
76     private static final int DEFAULT_PHONE_ID = 0;
77     private static final int DEFAULT_SUB_ID = SubscriptionManager.getDefaultSubscriptionId();
78     private static final String PLATFORM_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig";
79     private static final long PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE = 1;
80     private static final String CARRIER_CONFIG_EXAMPLE_KEY =
81             CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT;
82     private static final int CARRIER_CONFIG_EXAMPLE_VALUE =
83             CarrierConfigManager.USSD_OVER_CS_PREFERRED;
84 
85     @Mock Resources mResources;
86     @Mock PackageManager mPackageManager;
87     @Mock PackageInfo mPackageInfo;
88     @Mock SubscriptionInfoUpdater mSubscriptionInfoUpdater;
89     @Mock SharedPreferences mSharedPreferences;
90 
91     private TelephonyManager mTelephonyManager;
92     private CarrierConfigLoader mCarrierConfigLoader;
93     private Handler mHandler;
94     private HandlerThread mHandlerThread;
95     private TestableLooper mTestableLooper;
96 
97     @Before
setUp()98     public void setUp() throws Exception {
99         super.setUp();
100 
101         doReturn(mSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt());
102         doReturn(Build.FINGERPRINT).when(mSharedPreferences).getString(eq("build_fingerprint"),
103                 any());
104         doReturn(mPackageManager).when(mContext).getPackageManager();
105         doReturn(mResources).when(mContext).getResources();
106         doReturn(InstrumentationRegistry.getTargetContext().getFilesDir()).when(
107                 mContext).getFilesDir();
108         doReturn(PLATFORM_CARRIER_CONFIG_PACKAGE).when(mResources).getString(
109                 eq(R.string.platform_carrier_config_package));
110         mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
111         doReturn(1).when(mTelephonyManager).getSupportedModemCount();
112         doReturn(1).when(mTelephonyManager).getActiveModemCount();
113         doReturn("spn").when(mTelephonyManager).getSimOperatorNameForPhone(anyInt());
114         doReturn("310260").when(mTelephonyManager).getSimOperatorNumericForPhone(anyInt());
115         doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(
116                 eq(PLATFORM_CARRIER_CONFIG_PACKAGE), eq(0) /*flags*/);
117         doReturn(PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE).when(
118                 mPackageInfo).getLongVersionCode();
119 
120         mHandlerThread = new HandlerThread("CarrierConfigLoaderTest");
121         mHandlerThread.start();
122 
123         mTestableLooper = new TestableLooper(mHandlerThread.getLooper());
124         mCarrierConfigLoader = new CarrierConfigLoader(mContext, mSubscriptionInfoUpdater,
125                 mTestableLooper.getLooper());
126         mHandler = mCarrierConfigLoader.getHandler();
127 
128         // Clear all configs to have the same starting point.
129         mCarrierConfigLoader.clearConfigForPhone(DEFAULT_PHONE_ID, false);
130     }
131 
132     @After
tearDown()133     public void tearDown() throws Exception {
134         mContext.revokeAllPermissions();
135         mTestableLooper.destroy();
136         mHandlerThread.quit();
137         super.tearDown();
138     }
139 
140     /**
141      * Verifies that SecurityException should throw when call #updateConfigForPhoneId() without
142      * MODIFY_PHONE_STATE permission.
143      */
144     @Test
testUpdateConfigForPhoneId_noPermission()145     public void testUpdateConfigForPhoneId_noPermission() throws Exception {
146         assertThrows(SecurityException.class,
147                 () -> mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
148                         IccCardConstants.INTENT_VALUE_ICC_ABSENT));
149     }
150 
151     /**
152      * Verifies that IllegalArgumentException should throw when call #updateConfigForPhoneId() with
153      * invalid phoneId.
154      */
155     @Test
testUpdateConfigForPhoneId_invalidPhoneId()156     public void testUpdateConfigForPhoneId_invalidPhoneId() throws Exception {
157         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
158 
159         assertThrows(IllegalArgumentException.class,
160                 () -> mCarrierConfigLoader.updateConfigForPhoneId(
161                         SubscriptionManager.INVALID_PHONE_INDEX,
162                         IccCardConstants.INTENT_VALUE_ICC_ABSENT));
163     }
164 
165     /**
166      * Verifies that when call #updateConfigForPhoneId() with SIM absence, both carrier config from
167      * default app and carrier should be cleared but no-sim config should be loaded.
168      */
169     @Test
testUpdateConfigForPhoneId_simAbsent()170     public void testUpdateConfigForPhoneId_simAbsent() throws Exception {
171         // Bypass case if default subId is not supported by device to reduce flakiness
172         if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
173             return;
174         }
175         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
176         doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
177 
178         // Prepare a cached config to fetch from xml
179         PersistableBundle config = getTestConfig();
180         mCarrierConfigLoader.saveNoSimConfigToXml(PLATFORM_CARRIER_CONFIG_PACKAGE, config);
181         mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
182                 IccCardConstants.INTENT_VALUE_ICC_ABSENT);
183         mTestableLooper.processAllMessages();
184 
185         assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID)).isNull();
186         assertThat(mCarrierConfigLoader.getConfigFromCarrierApp(DEFAULT_PHONE_ID)).isNull();
187         assertThat(mCarrierConfigLoader.getNoSimConfig().getInt(CARRIER_CONFIG_EXAMPLE_KEY))
188                 .isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
189         verify(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
190     }
191 
192     /**
193      * Verifies that with cached config in XML, calling #updateConfigForPhoneId() with SIM loaded
194      * will return the right config in the XML.
195      */
196     @Test
testUpdateConfigForPhoneId_simLoaded_withCachedConfigInXml()197     public void testUpdateConfigForPhoneId_simLoaded_withCachedConfigInXml() throws Exception {
198         // Bypass case if default subId is not supported by device to reduce flakiness
199         if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
200             return;
201         }
202         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
203 
204         // Prepare to make sure we can save the config into the XML file which used as cache
205         List<String> carrierPackages = List.of(PLATFORM_CARRIER_CONFIG_PACKAGE);
206         doReturn(carrierPackages).when(mTelephonyManager).getCarrierPackageNamesForIntentAndPhone(
207                 nullable(Intent.class), anyInt());
208 
209         // Save the sample config into the XML file
210         PersistableBundle config = getTestConfig();
211         CarrierIdentifier carrierId = mCarrierConfigLoader.getCarrierIdentifierForPhoneId(
212                 DEFAULT_PHONE_ID);
213         mCarrierConfigLoader.saveConfigToXml(PLATFORM_CARRIER_CONFIG_PACKAGE, "",
214                 DEFAULT_PHONE_ID, carrierId, config);
215         mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
216                 IccCardConstants.INTENT_VALUE_ICC_LOADED);
217         mTestableLooper.processAllMessages();
218 
219         assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID).getInt(
220                 CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
221 
222     }
223 
224     /**
225      * Verifies that SecurityException should throw if call #overrideConfig() without
226      * MODIFY_PHONE_STATE permission.
227      */
228     @Test
testOverrideConfig_noPermission()229     public void testOverrideConfig_noPermission() throws Exception {
230         assertThrows(SecurityException.class,
231                 () -> mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, PersistableBundle.EMPTY,
232                         false));
233     }
234 
235     /**
236      * Verifies IllegalArgumentException should throw if call #overrideConfig() with invalid subId.
237      */
238     @Test
testOverrideConfig_invalidSubId()239     public void testOverrideConfig_invalidSubId() throws Exception {
240         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
241 
242         assertThrows(IllegalArgumentException.class, () -> mCarrierConfigLoader.overrideConfig(
243                 SubscriptionManager.INVALID_SUBSCRIPTION_ID, new PersistableBundle(), false));
244     }
245 
246     /**
247      * Verifies that override config is not null when calling #overrideConfig with null bundle.
248      */
249     @Test
testOverrideConfig_withNullBundle()250     public void testOverrideConfig_withNullBundle() throws Exception {
251         // Bypass case if default subId is not supported by device to reduce flakiness
252         if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
253             return;
254         }
255         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
256 
257         mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, null /*overrides*/,
258                 false/*persistent*/);
259         mTestableLooper.processAllMessages();
260 
261         assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).isEmpty()).isTrue();
262         verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
263                 eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
264                 any(PersistableBundle.class), any(Message.class));
265     }
266 
267     /**
268      * Verifies that override config is not null when calling #overrideConfig with non-null bundle.
269      */
270     @Test
testOverrideConfig_withNonNullBundle()271     public void testOverrideConfig_withNonNullBundle() throws Exception {
272         // Bypass case if default subId is not supported by device to reduce flakiness
273         if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
274             return;
275         }
276         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
277 
278         PersistableBundle config = getTestConfig();
279         mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, config /*overrides*/,
280                 false/*persistent*/);
281         mTestableLooper.processAllMessages();
282 
283         assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).getInt(
284                 CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
285         verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
286                 eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
287                 any(PersistableBundle.class), any(Message.class));
288     }
289 
290     /**
291      * Verifies that IllegalArgumentException should throw when calling
292      * #notifyConfigChangedForSubId() with invalid subId.
293      */
294     @Test
testNotifyConfigChangedForSubId_invalidSubId()295     public void testNotifyConfigChangedForSubId_invalidSubId() throws Exception {
296         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
297 
298         assertThrows(IllegalArgumentException.class,
299                 () -> mCarrierConfigLoader.notifyConfigChangedForSubId(
300                         SubscriptionManager.INVALID_SUBSCRIPTION_ID));
301     }
302 
303     // TODO(b/184040111): Enable test case when support disabling carrier privilege
304     // Phone/System UID always has carrier privilege (TelephonyPermission#getCarrierPrivilegeStatus)
305     // when running the test here.
306     /**
307      * Verifies that SecurityException should throw when calling notifyConfigChangedForSubId without
308      * MODIFY_PHONE_STATE permission.
309      */
310     @Ignore
testNotifyConfigChangedForSubId_noPermission()311     public void testNotifyConfigChangedForSubId_noPermission() throws Exception {
312         setCarrierPrivilegesForSubId(false, DEFAULT_SUB_ID);
313 
314         assertThrows(SecurityException.class,
315                 () -> mCarrierConfigLoader.notifyConfigChangedForSubId(DEFAULT_SUB_ID));
316     }
317 
318     /**
319      * Verifies that SecurityException should throw when calling getDefaultCarrierServicePackageName
320      * without READ_PRIVILEGED_PHONE_STATE permission.
321      */
322     @Test
testGetDefaultCarrierServicePackageName_noPermission()323     public void testGetDefaultCarrierServicePackageName_noPermission() {
324         assertThrows(SecurityException.class,
325                 () -> mCarrierConfigLoader.getDefaultCarrierServicePackageName());
326     }
327 
328     /**
329      * Verifies that the right default carrier service package name is return when calling
330      * getDefaultCarrierServicePackageName with permission.
331      */
332     @Test
testGetDefaultCarrierServicePackageName_withPermission()333     public void testGetDefaultCarrierServicePackageName_withPermission() {
334         mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
335 
336         assertThat(mCarrierConfigLoader.getDefaultCarrierServicePackageName())
337                 .isEqualTo(PLATFORM_CARRIER_CONFIG_PACKAGE);
338     }
339 
340     // TODO(b/184040111): Enable test case when support disabling carrier privilege
341     // Phone/System UID always has carrier privilege (TelephonyPermission#getCarrierPrivilegeStatus)
342     // when running the test here.
343     /**
344      * Verifies that without permission, #getConfigForSubId will return an empty PersistableBundle.
345      */
346     @Ignore
testGetConfigForSubId_noPermission()347     public void testGetConfigForSubId_noPermission() {
348         // Bypass case if default subId is not supported by device to reduce flakiness
349         if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
350             return;
351         }
352         setCarrierPrivilegesForSubId(false, DEFAULT_SUB_ID);
353 
354         assertThat(mCarrierConfigLoader.getConfigForSubId(DEFAULT_SUB_ID,
355                 PLATFORM_CARRIER_CONFIG_PACKAGE)).isEqualTo(PersistableBundle.EMPTY);
356     }
357 
358     /**
359      * Verifies that when have no DUMP permission, the #dump() method shows permission denial.
360      */
361     @Test
testDump_noPermission()362     public void testDump_noPermission() {
363         StringWriter stringWriter = new StringWriter();
364         mCarrierConfigLoader.dump(new FileDescriptor(), new PrintWriter(stringWriter),
365                 new String[0]);
366         stringWriter.flush();
367 
368         assertThat(stringWriter.toString()).contains("Permission Denial:");
369     }
370 
371     /**
372      * Verifies that when have DUMP permission, the #dump() method can dump the CarrierConfigLoader.
373      */
374     @Test
testDump_withPermission()375     public void testDump_withPermission() {
376         mContext.grantPermission(android.Manifest.permission.DUMP);
377 
378         StringWriter stringWriter = new StringWriter();
379         mCarrierConfigLoader.dump(new FileDescriptor(), new PrintWriter(stringWriter),
380                 new String[0]);
381         stringWriter.flush();
382 
383         String dumpContent = stringWriter.toString();
384         assertThat(dumpContent).contains("CarrierConfigLoader:");
385         assertThat(dumpContent).doesNotContain("Permission Denial:");
386     }
387 
getTestConfig()388     private static PersistableBundle getTestConfig() {
389         PersistableBundle config = new PersistableBundle();
390         config.putInt(CARRIER_CONFIG_EXAMPLE_KEY, CARRIER_CONFIG_EXAMPLE_VALUE);
391         return config;
392     }
393 
setCarrierPrivilegesForSubId(boolean hasCarrierPrivileges, int subId)394     private void setCarrierPrivilegesForSubId(boolean hasCarrierPrivileges, int subId) {
395         TelephonyManager mockTelephonyManager = Mockito.mock(TelephonyManager.class);
396         doReturn(mockTelephonyManager).when(mTelephonyManager).createForSubscriptionId(subId);
397         doReturn(hasCarrierPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
398                 : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS).when(
399                 mockTelephonyManager).getCarrierPrivilegeStatus(anyInt());
400     }
401 }
402