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