• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 
17 package com.android.networkstack.tethering;
18 
19 import static android.net.ConnectivityManager.TYPE_ETHERNET;
20 import static android.net.ConnectivityManager.TYPE_MOBILE;
21 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
22 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
23 import static android.net.ConnectivityManager.TYPE_WIFI;
24 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
25 import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
26 import static android.telephony.CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL;
27 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
28 
29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
31 import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
32 import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
33 import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
34 import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
35 
36 import static org.junit.Assert.assertArrayEquals;
37 import static org.junit.Assert.assertEquals;
38 import static org.junit.Assert.assertFalse;
39 import static org.junit.Assert.assertTrue;
40 import static org.mockito.ArgumentMatchers.anyInt;
41 import static org.mockito.Matchers.eq;
42 import static org.mockito.Mockito.when;
43 
44 import android.content.Context;
45 import android.content.pm.ApplicationInfo;
46 import android.content.pm.ModuleInfo;
47 import android.content.pm.PackageInfo;
48 import android.content.pm.PackageManager;
49 import android.content.res.Resources;
50 import android.net.util.SharedLog;
51 import android.os.Build;
52 import android.os.PersistableBundle;
53 import android.provider.DeviceConfig;
54 import android.provider.Settings;
55 import android.telephony.CarrierConfigManager;
56 import android.telephony.TelephonyManager;
57 import android.test.mock.MockContentResolver;
58 
59 import androidx.test.filters.SmallTest;
60 import androidx.test.runner.AndroidJUnit4;
61 
62 import com.android.internal.util.test.BroadcastInterceptingContext;
63 import com.android.internal.util.test.FakeSettingsProvider;
64 import com.android.modules.utils.build.SdkLevel;
65 import com.android.net.module.util.DeviceConfigUtils;
66 import com.android.testutils.DevSdkIgnoreRule;
67 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
68 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
69 
70 import org.junit.After;
71 import org.junit.Before;
72 import org.junit.Rule;
73 import org.junit.Test;
74 import org.junit.runner.RunWith;
75 import org.mockito.Mock;
76 import org.mockito.MockitoSession;
77 import org.mockito.quality.Strictness;
78 
79 import java.util.Arrays;
80 import java.util.Iterator;
81 
82 @RunWith(AndroidJUnit4.class)
83 @SmallTest
84 public class TetheringConfigurationTest {
85     private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
86 
87     @Rule public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
88 
89     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
90     private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
91     private static final String PROVISIONING_APP_RESPONSE = "app_response";
92     private static final String TEST_PACKAGE_NAME = "com.android.tethering.test";
93     private static final String APEX_NAME = "com.android.tethering";
94     private static final long TEST_PACKAGE_VERSION = 1234L;
95     @Mock private ApplicationInfo mApplicationInfo;
96     @Mock private Context mContext;
97     @Mock private CarrierConfigManager mCarrierConfigManager;
98     @Mock private TelephonyManager mTelephonyManager;
99     @Mock private Resources mResources;
100     @Mock private Resources mResourcesForSubId;
101     @Mock private PackageManager mPackageManager;
102     @Mock private ModuleInfo mMi;
103     private Context mMockContext;
104     private boolean mHasTelephonyManager;
105     private MockitoSession mMockingSession;
106     private MockContentResolver mContentResolver;
107     private final PersistableBundle mCarrierConfig = new PersistableBundle();
108 
109     private class MockTetheringConfiguration extends TetheringConfiguration {
MockTetheringConfiguration(Context ctx, SharedLog log, int id)110         MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
111             super(ctx, log, id);
112         }
113 
114         @Override
getResourcesForSubIdWrapper(Context ctx, int subId)115         protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
116             return mResourcesForSubId;
117         }
118     }
119 
120     private class MockContext extends BroadcastInterceptingContext {
MockContext(Context base)121         MockContext(Context base) {
122             super(base);
123         }
124 
125         @Override
getApplicationInfo()126         public ApplicationInfo getApplicationInfo() {
127             return mApplicationInfo;
128         }
129 
130         @Override
getResources()131         public Resources getResources() {
132             return mResources;
133         }
134 
135         @Override
getSystemService(String name)136         public Object getSystemService(String name) {
137             if (Context.TELEPHONY_SERVICE.equals(name)) {
138                 return mHasTelephonyManager ? mTelephonyManager : null;
139             }
140             return super.getSystemService(name);
141         }
142 
143         @Override
getPackageManager()144         public PackageManager getPackageManager() {
145             return mPackageManager;
146         }
147 
148         @Override
getPackageName()149         public String getPackageName() {
150             return TEST_PACKAGE_NAME;
151         }
152     }
153 
154     @Before
setUp()155     public void setUp() throws Exception {
156         // TODO: use a dependencies class instead of mock statics.
157         mMockingSession = mockitoSession()
158                 .initMocks(this)
159                 .mockStatic(DeviceConfig.class)
160                 .strictness(Strictness.WARN)
161                 .startMocking();
162         DeviceConfigUtils.resetPackageVersionCacheForTest();
163         doReturn(null).when(
164                 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
165                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
166         setTetherForceUpstreamAutomaticFlagVersion(null);
167 
168         final PackageInfo pi = new PackageInfo();
169         pi.setLongVersionCode(TEST_PACKAGE_VERSION);
170         doReturn(pi).when(mPackageManager).getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt());
171         doReturn(mMi).when(mPackageManager).getModuleInfo(eq(APEX_NAME), anyInt());
172         doReturn(TEST_PACKAGE_NAME).when(mMi).getPackageName();
173 
174         when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
175                 new String[0]);
176         when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
177                 TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
178         when(mResources.getStringArray(R.array.config_tether_usb_regexs))
179                 .thenReturn(new String[]{ "test_usb\\d" });
180         when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
181                 .thenReturn(new String[]{ "test_wlan\\d" });
182         when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn(
183                 new String[0]);
184         when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
185         when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
186                 .thenReturn(new String[0]);
187         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
188                 false);
189         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
190                 .thenReturn(false);
191         initializeBpfOffloadConfiguration(true, null /* unset */);
192 
193         mHasTelephonyManager = true;
194         mMockContext = new MockContext(mContext);
195 
196         mContentResolver = new MockContentResolver(mMockContext);
197         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
198         when(mContext.getContentResolver()).thenReturn(mContentResolver);
199         // Call {@link #clearSettingsProvider()} before and after using FakeSettingsProvider.
200         FakeSettingsProvider.clearSettingsProvider();
201     }
202 
203     @After
tearDown()204     public void tearDown() throws Exception {
205         mMockingSession.finishMocking();
206         DeviceConfigUtils.resetPackageVersionCacheForTest();
207         // Call {@link #clearSettingsProvider()} before and after using FakeSettingsProvider.
208         FakeSettingsProvider.clearSettingsProvider();
209     }
210 
getTetheringConfiguration(int... legacyTetherUpstreamTypes)211     private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
212         when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
213                 legacyTetherUpstreamTypes);
214         return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
215     }
216 
217     @Test
testNoTelephonyManagerMeansNoDun()218     public void testNoTelephonyManagerMeansNoDun() {
219         mHasTelephonyManager = false;
220         final TetheringConfiguration cfg = getTetheringConfiguration(
221                 new int[]{TYPE_MOBILE_DUN, TYPE_WIFI});
222         assertFalse(cfg.isDunRequired);
223         assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
224         // Just to prove we haven't clobbered Wi-Fi:
225         assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
226     }
227 
228     @Test
testDunFromTelephonyManagerMeansDun()229     public void testDunFromTelephonyManagerMeansDun() {
230         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true);
231 
232         final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
233         final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
234                 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
235         final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
236                 TYPE_WIFI, TYPE_MOBILE_DUN);
237         final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
238                 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
239 
240         for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri,
241                 cfgWifiDun, cfgMobileWifiHipriDun)) {
242             String msg = "config=" + cfg.toString();
243             assertTrue(msg, cfg.isDunRequired);
244             assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
245             assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
246             assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
247             // Just to prove we haven't clobbered Wi-Fi:
248             assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
249         }
250     }
251 
252     @Test
testDunNotRequiredFromTelephonyManagerMeansNoDun()253     public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
254         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
255 
256         final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
257         final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
258                 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
259         final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
260                 TYPE_WIFI, TYPE_MOBILE_DUN);
261         final TetheringConfiguration cfgWifiMobile = getTetheringConfiguration(
262                 TYPE_WIFI, TYPE_MOBILE);
263         final TetheringConfiguration cfgWifiHipri = getTetheringConfiguration(
264                 TYPE_WIFI, TYPE_MOBILE_HIPRI);
265         final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
266                 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
267 
268         String msg;
269         // TYPE_MOBILE_DUN should be present in none of the combinations.
270         // TYPE_WIFI should not be affected.
271         for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun,
272                 cfgWifiMobile, cfgWifiHipri, cfgMobileWifiHipriDun)) {
273             msg = "config=" + cfg.toString();
274             assertFalse(msg, cfg.isDunRequired);
275             assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
276             assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
277         }
278 
279         for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun,
280                 cfgMobileWifiHipriDun)) {
281             msg = "config=" + cfg.toString();
282             assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
283             assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
284         }
285         msg = "config=" + cfgWifiMobile.toString();
286         assertTrue(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
287         assertFalse(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
288         msg = "config=" + cfgWifiHipri.toString();
289         assertFalse(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
290         assertTrue(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
291 
292     }
293 
294     @Test
testNoDefinedUpstreamTypesAddsEthernet()295     public void testNoDefinedUpstreamTypesAddsEthernet() {
296         when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{});
297         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
298 
299         final TetheringConfiguration cfg = new TetheringConfiguration(
300                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
301         final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
302         assertTrue(upstreamIterator.hasNext());
303         assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
304         // The following is because the code always adds some kind of mobile
305         // upstream, be it DUN or, in this case where DUN is NOT required,
306         // make sure there is at least one of MOBILE or HIPRI. With the empty
307         // list of the configuration in this test, it will always add both
308         // MOBILE and HIPRI, in that order.
309         assertTrue(upstreamIterator.hasNext());
310         assertEquals(TYPE_MOBILE, upstreamIterator.next().intValue());
311         assertTrue(upstreamIterator.hasNext());
312         assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
313         assertFalse(upstreamIterator.hasNext());
314     }
315 
316     @Test
testDefinedUpstreamTypesSansEthernetAddsEthernet()317     public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
318         when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
319                 new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
320         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
321 
322         final TetheringConfiguration cfg = new TetheringConfiguration(
323                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
324         final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
325         assertTrue(upstreamIterator.hasNext());
326         assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
327         assertTrue(upstreamIterator.hasNext());
328         assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
329         assertTrue(upstreamIterator.hasNext());
330         assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
331         assertFalse(upstreamIterator.hasNext());
332     }
333 
334     @Test
testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet()335     public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
336         when(mResources.getIntArray(R.array.config_tether_upstream_types))
337                 .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
338         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
339 
340         final TetheringConfiguration cfg = new TetheringConfiguration(
341                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
342         final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
343         assertTrue(upstreamIterator.hasNext());
344         assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
345         assertTrue(upstreamIterator.hasNext());
346         assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
347         assertTrue(upstreamIterator.hasNext());
348         assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
349         assertFalse(upstreamIterator.hasNext());
350     }
351 
initializeBpfOffloadConfiguration( final boolean fromRes, final String fromDevConfig)352     private void initializeBpfOffloadConfiguration(
353             final boolean fromRes, final String fromDevConfig) {
354         when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes);
355         doReturn(fromDevConfig).when(
356                 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
357                 eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD)));
358     }
359 
360     @Test
testBpfOffloadEnabledByResource()361     public void testBpfOffloadEnabledByResource() {
362         initializeBpfOffloadConfiguration(true, null /* unset */);
363         final TetheringConfiguration enableByRes =
364                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
365         assertTrue(enableByRes.isBpfOffloadEnabled());
366     }
367 
368     @Test
testBpfOffloadEnabledByDeviceConfigOverride()369     public void testBpfOffloadEnabledByDeviceConfigOverride() {
370         for (boolean res : new boolean[]{true, false}) {
371             initializeBpfOffloadConfiguration(res, "true");
372             final TetheringConfiguration enableByDevConOverride =
373                     new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
374             assertTrue(enableByDevConOverride.isBpfOffloadEnabled());
375         }
376     }
377 
378     @Test
testBpfOffloadDisabledByResource()379     public void testBpfOffloadDisabledByResource() {
380         initializeBpfOffloadConfiguration(false, null /* unset */);
381         final TetheringConfiguration disableByRes =
382                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
383         assertFalse(disableByRes.isBpfOffloadEnabled());
384     }
385 
386     @Test
testBpfOffloadDisabledByDeviceConfigOverride()387     public void testBpfOffloadDisabledByDeviceConfigOverride() {
388         for (boolean res : new boolean[]{true, false}) {
389             initializeBpfOffloadConfiguration(res, "false");
390             final TetheringConfiguration disableByDevConOverride =
391                     new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
392             assertFalse(disableByDevConOverride.isBpfOffloadEnabled());
393         }
394     }
395 
396     @Test
testNewDhcpServerDisabled()397     public void testNewDhcpServerDisabled() {
398         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
399                 true);
400         doReturn("false").when(
401                 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
402                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
403 
404         final TetheringConfiguration enableByRes =
405                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
406         assertTrue(enableByRes.useLegacyDhcpServer());
407 
408         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
409                 false);
410         doReturn("true").when(
411                 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
412                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
413 
414         final TetheringConfiguration enableByDevConfig =
415                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
416         assertTrue(enableByDevConfig.useLegacyDhcpServer());
417     }
418 
419     @Test
testNewDhcpServerEnabled()420     public void testNewDhcpServerEnabled() {
421         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
422                 false);
423         doReturn("false").when(
424                 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
425                 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
426 
427         final TetheringConfiguration cfg =
428                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
429 
430         assertFalse(cfg.useLegacyDhcpServer());
431     }
432 
433     @Test
testOffloadIntervalByResource()434     public void testOffloadIntervalByResource() {
435         final TetheringConfiguration intervalByDefault =
436                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
437         assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS,
438                 intervalByDefault.getOffloadPollInterval());
439 
440         final int[] testOverrides = {0, 3000, -1};
441         for (final int override : testOverrides) {
442             when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
443                     override);
444             final TetheringConfiguration overrideByRes =
445                     new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
446             assertEquals(override, overrideByRes.getOffloadPollInterval());
447         }
448     }
449 
450     @Test
testGetResourcesBySubId()451     public void testGetResourcesBySubId() {
452         setUpResourceForSubId();
453         final TetheringConfiguration cfg = new TetheringConfiguration(
454                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
455         assertTrue(cfg.provisioningApp.length == 0);
456         final int anyValidSubId = 1;
457         final MockTetheringConfiguration mockCfg =
458                 new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
459         assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
460         assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
461         assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME);
462         assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE);
463     }
464 
setUpResourceForSubId()465     private void setUpResourceForSubId() {
466         when(mResourcesForSubId.getStringArray(
467                 R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
468         when(mResourcesForSubId.getStringArray(
469                 R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
470         when(mResourcesForSubId.getStringArray(
471                 R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
472         when(mResourcesForSubId.getStringArray(
473                 R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]);
474         when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
475                 new int[0]);
476         when(mResourcesForSubId.getStringArray(
477                 R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
478         when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
479                 .thenReturn(PROVISIONING_NO_UI_APP_NAME);
480         when(mResourcesForSubId.getString(
481                 R.string.config_mobile_hotspot_provision_response)).thenReturn(
482                 PROVISIONING_APP_RESPONSE);
483     }
484 
mockService(String serviceName, Class<T> serviceClass, T service)485     private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
486         when(mMockContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
487         when(mMockContext.getSystemService(serviceName)).thenReturn(service);
488     }
489 
490     @Test
testGetCarrierConfigBySubId_noCarrierConfigManager_configsAreDefault()491     public void testGetCarrierConfigBySubId_noCarrierConfigManager_configsAreDefault() {
492         // Act like the CarrierConfigManager is present and ready unless told otherwise.
493         mockService(Context.CARRIER_CONFIG_SERVICE,
494                 CarrierConfigManager.class, null);
495         final TetheringConfiguration cfg = new TetheringConfiguration(
496                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
497 
498         assertTrue(cfg.isCarrierSupportTethering);
499         assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
500     }
501 
502     @Test
testGetCarrierConfigBySubId_carrierConfigMissing_configsAreDefault()503     public void testGetCarrierConfigBySubId_carrierConfigMissing_configsAreDefault() {
504         // Act like the CarrierConfigManager is present and ready unless told otherwise.
505         mockService(Context.CARRIER_CONFIG_SERVICE,
506                 CarrierConfigManager.class, mCarrierConfigManager);
507         when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
508         final TetheringConfiguration cfg = new TetheringConfiguration(
509                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
510 
511         assertTrue(cfg.isCarrierSupportTethering);
512         assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
513     }
514 
515     @Test
testGetCarrierConfigBySubId_hasConfigs_carrierUnsupportAndCheckNotRequired()516     public void testGetCarrierConfigBySubId_hasConfigs_carrierUnsupportAndCheckNotRequired() {
517         mockService(Context.CARRIER_CONFIG_SERVICE,
518                 CarrierConfigManager.class, mCarrierConfigManager);
519         mCarrierConfig.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
520         mCarrierConfig.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, false);
521         mCarrierConfig.putBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, false);
522         when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
523         final TetheringConfiguration cfg = new TetheringConfiguration(
524                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
525 
526         if (SdkLevel.isAtLeastT()) {
527             assertFalse(cfg.isCarrierSupportTethering);
528         } else {
529             assertTrue(cfg.isCarrierSupportTethering);
530         }
531         assertFalse(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
532 
533     }
534 
535     @Test
testEnableLegacyWifiP2PAddress()536     public void testEnableLegacyWifiP2PAddress() throws Exception {
537         final TetheringConfiguration defaultCfg = new TetheringConfiguration(
538                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
539         assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp());
540 
541         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
542                 .thenReturn(true);
543         final TetheringConfiguration testCfg = new TetheringConfiguration(
544                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
545         assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
546     }
547 
548     @Test
testChooseUpstreamAutomatically()549     public void testChooseUpstreamAutomatically() throws Exception {
550         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
551                 .thenReturn(true);
552         assertChooseUpstreamAutomaticallyIs(true);
553 
554         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
555                 .thenReturn(false);
556         assertChooseUpstreamAutomaticallyIs(false);
557     }
558 
559     // The flag override only works on R-
560     @Test @IgnoreAfter(Build.VERSION_CODES.R)
testChooseUpstreamAutomatically_FlagOverride()561     public void testChooseUpstreamAutomatically_FlagOverride() throws Exception {
562         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
563                 .thenReturn(false);
564         setTetherForceUpstreamAutomaticFlagVersion(TEST_PACKAGE_VERSION - 1);
565         assertTrue(DeviceConfigUtils.isFeatureEnabled(mMockContext, NAMESPACE_CONNECTIVITY,
566                 TetheringConfiguration.TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION, APEX_NAME, false));
567 
568         assertChooseUpstreamAutomaticallyIs(true);
569 
570         setTetherForceUpstreamAutomaticFlagVersion(0L);
571         assertChooseUpstreamAutomaticallyIs(false);
572 
573         setTetherForceUpstreamAutomaticFlagVersion(Long.MAX_VALUE);
574         assertChooseUpstreamAutomaticallyIs(false);
575     }
576 
577     @Test @IgnoreUpTo(Build.VERSION_CODES.R)
testChooseUpstreamAutomatically_FlagOverrideAfterR()578     public void testChooseUpstreamAutomatically_FlagOverrideAfterR() throws Exception {
579         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
580                 .thenReturn(false);
581         setTetherForceUpstreamAutomaticFlagVersion(TEST_PACKAGE_VERSION - 1);
582         assertChooseUpstreamAutomaticallyIs(false);
583     }
584 
setTetherForceUpstreamAutomaticFlagVersion(Long version)585     private void setTetherForceUpstreamAutomaticFlagVersion(Long version) {
586         doReturn(version == null ? null : Long.toString(version)).when(
587                 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
588                         eq(TetheringConfiguration.TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION)));
589     }
590 
assertChooseUpstreamAutomaticallyIs(boolean value)591     private void assertChooseUpstreamAutomaticallyIs(boolean value) {
592         assertEquals(value, new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)
593                 .chooseUpstreamAutomatically);
594     }
595 
596     @Test
testUsbTetheringFunctions()597     public void testUsbTetheringFunctions() throws Exception {
598         // Test default value. If both resource and settings is not configured, usingNcm is false.
599         assertIsUsingNcm(false /* usingNcm */);
600 
601         when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
602                 TETHER_USB_NCM_FUNCTION);
603         assertIsUsingNcm(true /* usingNcm */);
604 
605         when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
606                 TETHER_USB_RNDIS_FUNCTION);
607         assertIsUsingNcm(false /* usingNcm */);
608 
609         setTetherForceUsbFunctions(TETHER_USB_RNDIS_FUNCTION);
610         assertIsUsingNcm(false /* usingNcm */);
611 
612         setTetherForceUsbFunctions(TETHER_USB_NCM_FUNCTION);
613         assertIsUsingNcm(true /* usingNcm */);
614 
615         // Test throws NumberFormatException.
616         setTetherForceUsbFunctions("WrongNumberFormat");
617         assertIsUsingNcm(false /* usingNcm */);
618     }
619 
assertIsUsingNcm(boolean expected)620     private void assertIsUsingNcm(boolean expected) {
621         final TetheringConfiguration cfg =
622                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
623         assertEquals(expected, cfg.isUsingNcm());
624     }
625 
setTetherForceUsbFunctions(final String value)626     private void setTetherForceUsbFunctions(final String value) {
627         Settings.Global.putString(mContentResolver, TETHER_FORCE_USB_FUNCTIONS, value);
628     }
629 
setTetherForceUsbFunctions(final int value)630     private void setTetherForceUsbFunctions(final int value) {
631         setTetherForceUsbFunctions(Integer.toString(value));
632     }
633 
634     @Test
testNcmRegexs()635     public void testNcmRegexs() throws Exception {
636         final String[] rndisRegexs = {"test_rndis\\d"};
637         final String[] ncmRegexs   = {"test_ncm\\d"};
638         final String[] rndisNcmRegexs   = {"test_rndis\\d", "test_ncm\\d"};
639 
640         // cfg.isUsingNcm = false.
641         when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
642                 TETHER_USB_RNDIS_FUNCTION);
643         setUsbAndNcmRegexs(rndisRegexs, ncmRegexs);
644         assertUsbAndNcmRegexs(rndisRegexs, ncmRegexs);
645 
646         setUsbAndNcmRegexs(rndisNcmRegexs, new String[0]);
647         assertUsbAndNcmRegexs(rndisNcmRegexs, new String[0]);
648 
649         // cfg.isUsingNcm = true.
650         when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
651                 TETHER_USB_NCM_FUNCTION);
652         setUsbAndNcmRegexs(rndisRegexs, ncmRegexs);
653         assertUsbAndNcmRegexs(ncmRegexs, new String[0]);
654 
655         setUsbAndNcmRegexs(rndisNcmRegexs, new String[0]);
656         assertUsbAndNcmRegexs(rndisNcmRegexs, new String[0]);
657 
658         // Check USB regex is not overwritten by the NCM regex after force to use rndis from
659         // Settings.
660         setUsbAndNcmRegexs(rndisRegexs, ncmRegexs);
661         setTetherForceUsbFunctions(TETHER_USB_RNDIS_FUNCTION);
662         assertUsbAndNcmRegexs(rndisRegexs, ncmRegexs);
663     }
664 
setUsbAndNcmRegexs(final String[] usbRegexs, final String[] ncmRegexs)665     private void setUsbAndNcmRegexs(final String[] usbRegexs, final String[] ncmRegexs) {
666         when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(usbRegexs);
667         when(mResources.getStringArray(R.array.config_tether_ncm_regexs)).thenReturn(ncmRegexs);
668     }
669 
assertUsbAndNcmRegexs(final String[] usbRegexs, final String[] ncmRegexs)670     private void assertUsbAndNcmRegexs(final String[] usbRegexs, final String[] ncmRegexs) {
671         final TetheringConfiguration cfg =
672                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
673         assertArrayEquals(usbRegexs, cfg.tetherableUsbRegexs);
674         assertArrayEquals(ncmRegexs, cfg.tetherableNcmRegexs);
675     }
676 
677     @Test
testP2pLeasesSubnetPrefixLength()678     public void testP2pLeasesSubnetPrefixLength() throws Exception {
679         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
680                 .thenReturn(true);
681 
682         final int defaultSubnetPrefixLength = 0;
683         final TetheringConfiguration defaultCfg =
684                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
685         assertEquals(defaultSubnetPrefixLength, defaultCfg.getP2pLeasesSubnetPrefixLength());
686 
687         final int prefixLengthTooSmall = -1;
688         when(mResources.getInteger(R.integer.config_p2p_leases_subnet_prefix_length)).thenReturn(
689                 prefixLengthTooSmall);
690         final TetheringConfiguration tooSmallCfg =
691                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
692         assertEquals(defaultSubnetPrefixLength, tooSmallCfg.getP2pLeasesSubnetPrefixLength());
693 
694         final int prefixLengthTooLarge = 31;
695         when(mResources.getInteger(R.integer.config_p2p_leases_subnet_prefix_length)).thenReturn(
696                 prefixLengthTooLarge);
697         final TetheringConfiguration tooLargeCfg =
698                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
699         assertEquals(defaultSubnetPrefixLength, tooLargeCfg.getP2pLeasesSubnetPrefixLength());
700 
701         final int p2pLeasesSubnetPrefixLength = 27;
702         when(mResources.getInteger(R.integer.config_p2p_leases_subnet_prefix_length)).thenReturn(
703                 p2pLeasesSubnetPrefixLength);
704         final TetheringConfiguration p2pCfg =
705                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
706         assertEquals(p2pLeasesSubnetPrefixLength, p2pCfg.getP2pLeasesSubnetPrefixLength());
707     }
708 }
709