[Tether02] Migrate TetheringConfiguration into module

TetheringConfiguration is a utility class to encapsulate the various
configuration elements.

Bug: 136040414
Test: -build, flash, boot
      -atest TetheringTests

Change-Id: I9434ab213bc5e0fca59f14a6c8cea554abefc3a4
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
new file mode 100644
index 0000000..089bbd3
--- /dev/null
+++ b/Tethering/tests/unit/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "TetheringTests",
+    certificate: "platform",
+    srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
+    static_libs: [
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "mockito-target-extended-minus-junit4",
+        "TetheringApiCurrentLib",
+        "testables",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+}
+
+// This group would be removed when tethering migration is done.
+filegroup {
+    name: "tethering-tests-src",
+    srcs: [
+        "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
+    ],
+}
diff --git a/Tethering/tests/unit/AndroidManifest.xml b/Tethering/tests/unit/AndroidManifest.xml
new file mode 100644
index 0000000..049ff6d
--- /dev/null
+++ b/Tethering/tests/unit/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.tethering.tests.unit">
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.tethering.tests.unit"
+        android:label="Tethering service tests">
+    </instrumentation>
+</manifest>
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
new file mode 100644
index 0000000..9f9221f
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
+import static com.android.internal.R.array.config_tether_bluetooth_regexs;
+import static com.android.internal.R.array.config_tether_dhcp_range;
+import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_wifi_regexs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringConfigurationTest {
+    private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
+
+    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+    @Mock private Context mContext;
+    @Mock private TelephonyManager mTelephonyManager;
+    @Mock private Resources mResources;
+    @Mock private Resources mResourcesForSubId;
+    private MockContentResolver mContentResolver;
+    private Context mMockContext;
+    private boolean mHasTelephonyManager;
+
+    private class MockTetheringConfiguration extends TetheringConfiguration {
+        MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
+            super(ctx, log, id);
+        }
+
+        @Override
+        protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
+            return mResourcesForSubId;
+        }
+    }
+
+    private class MockContext extends BroadcastInterceptingContext {
+        MockContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResources;
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.TELEPHONY_SERVICE.equals(name)) {
+                return mHasTelephonyManager ? mTelephonyManager : null;
+            }
+            return super.getSystemService(name);
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mContentResolver;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
+        when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
+        when(mResources.getStringArray(config_tether_wifi_regexs))
+                .thenReturn(new String[]{ "test_wlan\\d" });
+        when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]);
+        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
+        when(mResources.getStringArray(config_mobile_hotspot_provision_app))
+                .thenReturn(new String[0]);
+        mContentResolver = new MockContentResolver();
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        mHasTelephonyManager = true;
+        mMockContext = new MockContext(mContext);
+    }
+
+    private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
+        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+                legacyTetherUpstreamTypes);
+        return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+    }
+
+    @Test
+    public void testNoTelephonyManagerMeansNoDun() {
+        mHasTelephonyManager = false;
+        final TetheringConfiguration cfg = getTetheringConfiguration(
+                new int[]{TYPE_MOBILE_DUN, TYPE_WIFI});
+        assertFalse(cfg.isDunRequired);
+        assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+        // Just to prove we haven't clobbered Wi-Fi:
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+    }
+
+    @Test
+    public void testDunFromTelephonyManagerMeansDun() {
+        when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(true);
+
+        final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
+        final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
+                TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
+        final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
+                TYPE_WIFI, TYPE_MOBILE_DUN);
+        final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
+                TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
+
+        for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri,
+                cfgWifiDun, cfgMobileWifiHipriDun)) {
+            String msg = "config=" + cfg.toString();
+            assertTrue(msg, cfg.isDunRequired);
+            assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+            assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+            assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+            // Just to prove we haven't clobbered Wi-Fi:
+            assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+        }
+    }
+
+    @Test
+    public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
+        when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false);
+
+        final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
+        final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
+                TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
+        final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
+                TYPE_WIFI, TYPE_MOBILE_DUN);
+        final TetheringConfiguration cfgWifiMobile = getTetheringConfiguration(
+                TYPE_WIFI, TYPE_MOBILE);
+        final TetheringConfiguration cfgWifiHipri = getTetheringConfiguration(
+                TYPE_WIFI, TYPE_MOBILE_HIPRI);
+        final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
+                TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
+
+        String msg;
+        // TYPE_MOBILE_DUN should be present in none of the combinations.
+        // TYPE_WIFI should not be affected.
+        for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun,
+                cfgWifiMobile, cfgWifiHipri, cfgMobileWifiHipriDun)) {
+            msg = "config=" + cfg.toString();
+            assertFalse(msg, cfg.isDunRequired);
+            assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+            assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+        }
+
+        for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun,
+                cfgMobileWifiHipriDun)) {
+            msg = "config=" + cfg.toString();
+            assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+            assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+        }
+        msg = "config=" + cfgWifiMobile.toString();
+        assertTrue(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+        assertFalse(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+        msg = "config=" + cfgWifiHipri.toString();
+        assertFalse(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+        assertTrue(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+
+    }
+
+    @Test
+    public void testNoDefinedUpstreamTypesAddsEthernet() {
+        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{});
+        when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
+        // The following is because the code always adds some kind of mobile
+        // upstream, be it DUN or, in this case where DUN is NOT required,
+        // make sure there is at least one of MOBILE or HIPRI. With the empty
+        // list of the configuration in this test, it will always add both
+        // MOBILE and HIPRI, in that order.
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
+        assertFalse(upstreamIterator.hasNext());
+    }
+
+    @Test
+    public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
+        when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+                new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
+        when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
+        assertFalse(upstreamIterator.hasNext());
+    }
+
+    @Test
+    public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
+        when(mResources.getIntArray(config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
+        when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
+        assertFalse(upstreamIterator.hasNext());
+    }
+
+    @Test
+    public void testNewDhcpServerDisabled() {
+        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(cfg.enableLegacyDhcpServer);
+    }
+
+    @Test
+    public void testNewDhcpServerEnabled() {
+        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(cfg.enableLegacyDhcpServer);
+    }
+
+    @Test
+    public void testGetResourcesBySubId() {
+        setUpResourceForSubId();
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(cfg.provisioningApp.length == 0);
+        final int anyValidSubId = 1;
+        final MockTetheringConfiguration mockCfg =
+                new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
+        assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
+        assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
+    }
+
+    private void setUpResourceForSubId() {
+        when(mResourcesForSubId.getStringArray(
+                config_tether_dhcp_range)).thenReturn(new String[0]);
+        when(mResourcesForSubId.getStringArray(
+                config_tether_usb_regexs)).thenReturn(new String[0]);
+        when(mResourcesForSubId.getStringArray(
+                config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
+        when(mResourcesForSubId.getStringArray(
+                config_tether_bluetooth_regexs)).thenReturn(new String[0]);
+        when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
+        when(mResourcesForSubId.getStringArray(
+                config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
+    }
+
+}