[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
This commit is contained in:
markchien 2019-09-03 15:58:06 +08:00
commit 43e97e01b7
5 changed files with 785 additions and 0 deletions

View file

@ -19,6 +19,8 @@ java_defaults {
platform_apis: true,
srcs: [
"src/**/*.java",
":framework-tethering-shared-srcs",
":services-tethering-shared-srcs",
],
static_libs: [
"androidx.annotation_annotation",
@ -60,3 +62,11 @@ android_app {
// The permission configuration *must* be included to ensure security of the device
required: ["NetworkPermissionConfig"],
}
// This group will be removed when tethering migration is done.
filegroup {
name: "tethering-services-srcs",
srcs: [
"src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
],
}

View file

@ -0,0 +1,387 @@
/*
* 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.content.Context.TELEPHONY_SERVICE;
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.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
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_p2p_regexs;
import static com.android.internal.R.array.config_tether_wifi_regexs;
import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.util.SharedLog;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.StringJoiner;
/**
* A utility class to encapsulate the various tethering configuration elements.
*
* This configuration data includes elements describing upstream properties
* (preferred and required types of upstream connectivity as well as default
* DNS servers to use if none are available) and downstream properties (such
* as regular expressions use to match suitable downstream interfaces and the
* DHCPv4 ranges to use).
*
* @hide
*/
public class TetheringConfiguration {
private static final String TAG = TetheringConfiguration.class.getSimpleName();
private static final String[] EMPTY_STRING_ARRAY = new String[0];
// Default ranges used for the legacy DHCP server.
// USB is 192.168.42.1 and 255.255.255.0
// Wifi is 192.168.43.1 and 255.255.255.0
// BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
// with 255.255.255.0
// P2P is 192.168.49.1 and 255.255.255.0
private static final String[] LEGACY_DHCP_DEFAULT_RANGE = {
"192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
"192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
"192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
"192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
};
private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
public final String[] tetherableUsbRegexs;
public final String[] tetherableWifiRegexs;
public final String[] tetherableWifiP2pRegexs;
public final String[] tetherableBluetoothRegexs;
public final boolean isDunRequired;
public final boolean chooseUpstreamAutomatically;
public final Collection<Integer> preferredUpstreamIfaceTypes;
public final String[] legacyDhcpRanges;
public final String[] defaultIPv4DNS;
public final boolean enableLegacyDhcpServer;
public final String[] provisioningApp;
public final String provisioningAppNoUi;
public final int provisioningCheckPeriod;
public final int subId;
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
final SharedLog configLog = log.forSubComponent("config");
subId = id;
Resources res = getResources(ctx, subId);
tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
isDunRequired = checkDunRequired(ctx, subId);
chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
legacyDhcpRanges = getLegacyDhcpRanges(res);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx);
provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
provisioningAppNoUi = getProvisioningAppNoUi(res);
provisioningCheckPeriod = getResourceInteger(res,
config_mobile_hotspot_provision_check_period,
0 /* No periodic re-check */);
configLog.log(toString());
}
/** Check whether input interface belong to usb.*/
public boolean isUsb(String iface) {
return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
}
/** Check whether input interface belong to wifi.*/
public boolean isWifi(String iface) {
return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
}
/** Check whether this interface is Wifi P2P interface. */
public boolean isWifiP2p(String iface) {
return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
}
/** Check whether using legacy mode for wifi P2P. */
public boolean isWifiP2pLegacyTetheringMode() {
return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
}
/** Check whether input interface belong to bluetooth.*/
public boolean isBluetooth(String iface) {
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
/** Check whether no ui entitlement application is available.*/
public boolean hasMobileHotspotProvisionApp() {
return !TextUtils.isEmpty(provisioningAppNoUi);
}
/** Does the dumping.*/
public void dump(PrintWriter pw) {
pw.print("subId: ");
pw.println(subId);
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
pw.print("isDunRequired: ");
pw.println(isDunRequired);
pw.print("chooseUpstreamAutomatically: ");
pw.println(chooseUpstreamAutomatically);
dumpStringArray(pw, "preferredUpstreamIfaceTypes",
preferredUpstreamNames(preferredUpstreamIfaceTypes));
dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
dumpStringArray(pw, "provisioningApp", provisioningApp);
pw.print("provisioningAppNoUi: ");
pw.println(provisioningAppNoUi);
pw.print("enableLegacyDhcpServer: ");
pw.println(enableLegacyDhcpServer);
}
/** Returns the string representation of this object.*/
public String toString() {
final StringJoiner sj = new StringJoiner(" ");
sj.add(String.format("subId:%d", subId));
sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
sj.add(String.format("tetherableBluetoothRegexs:%s",
makeString(tetherableBluetoothRegexs)));
sj.add(String.format("isDunRequired:%s", isDunRequired));
sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
return String.format("TetheringConfiguration{%s}", sj.toString());
}
private static void dumpStringArray(PrintWriter pw, String label, String[] values) {
pw.print(label);
pw.print(": ");
if (values != null) {
final StringJoiner sj = new StringJoiner(", ", "[", "]");
for (String value : values) sj.add(value);
pw.print(sj.toString());
} else {
pw.print("null");
}
pw.println();
}
private static String makeString(String[] strings) {
if (strings == null) return "null";
final StringJoiner sj = new StringJoiner(",", "[", "]");
for (String s : strings) sj.add(s);
return sj.toString();
}
private static String[] preferredUpstreamNames(Collection<Integer> upstreamTypes) {
String[] upstreamNames = null;
if (upstreamTypes != null) {
upstreamNames = new String[upstreamTypes.size()];
int i = 0;
for (Integer netType : upstreamTypes) {
upstreamNames[i] = ConnectivityManager.getNetworkTypeName(netType);
i++;
}
}
return upstreamNames;
}
/** Check whether dun is required. */
public static boolean checkDunRequired(Context ctx, int id) {
final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
return (tm != null) ? tm.getTetherApnRequired(id) : false;
}
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
for (int i : ifaceTypes) {
switch (i) {
case TYPE_MOBILE:
case TYPE_MOBILE_HIPRI:
if (dunRequired) continue;
break;
case TYPE_MOBILE_DUN:
if (!dunRequired) continue;
break;
}
upstreamIfaceTypes.add(i);
}
// Fix up upstream interface types for DUN or mobile. NOTE: independent
// of the value of |dunRequired|, cell data of one form or another is
// *always* an upstream, regardless of the upstream interface types
// specified by configuration resources.
if (dunRequired) {
appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN);
} else {
// Do not modify if a cellular interface type is already present in the
// upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no
// cellular interface types are found in the upstream interface types.
// This preserves backwards compatibility and prevents the DUN and default
// mobile types incorrectly appearing together, which could happen on
// previous releases in the common case where checkDunRequired returned
// DUN_UNSPECIFIED.
if (!containsOneOf(upstreamIfaceTypes, TYPE_MOBILE, TYPE_MOBILE_HIPRI)) {
upstreamIfaceTypes.add(TYPE_MOBILE);
upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
}
}
// Always make sure our good friend Ethernet is present.
// TODO: consider unilaterally forcing this at the front.
prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET);
return upstreamIfaceTypes;
}
private static boolean matchesDownstreamRegexs(String iface, String[] regexs) {
for (String regex : regexs) {
if (iface.matches(regex)) return true;
}
return false;
}
private static String[] getLegacyDhcpRanges(Resources res) {
final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range);
if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
return fromResource;
}
return copy(LEGACY_DHCP_DEFAULT_RANGE);
}
private static String getProvisioningAppNoUi(Resources res) {
try {
return res.getString(config_mobile_hotspot_provision_app_no_ui);
} catch (Resources.NotFoundException e) {
return "";
}
}
private static boolean getResourceBoolean(Resources res, int resId) {
try {
return res.getBoolean(resId);
} catch (Resources.NotFoundException e404) {
return false;
}
}
private static String[] getResourceStringArray(Resources res, int resId) {
try {
final String[] strArray = res.getStringArray(resId);
return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
} catch (Resources.NotFoundException e404) {
return EMPTY_STRING_ARRAY;
}
}
private static int getResourceInteger(Resources res, int resId, int defaultValue) {
try {
return res.getInteger(resId);
} catch (Resources.NotFoundException e404) {
return defaultValue;
}
}
private static boolean getEnableLegacyDhcpServer(Context ctx) {
final ContentResolver cr = ctx.getContentResolver();
final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
return intVal != 0;
}
private Resources getResources(Context ctx, int subId) {
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return getResourcesForSubIdWrapper(ctx, subId);
} else {
return ctx.getResources();
}
}
@VisibleForTesting
protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
return SubscriptionManager.getResourcesForSubId(ctx, subId);
}
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
private static void prependIfNotPresent(ArrayList<Integer> list, int value) {
if (list.contains(value)) return;
list.add(0, value);
}
private static void appendIfNotPresent(ArrayList<Integer> list, int value) {
if (list.contains(value)) return;
list.add(value);
}
private static boolean containsOneOf(ArrayList<Integer> list, Integer... values) {
for (Integer value : values) {
if (list.contains(value)) return true;
}
return false;
}
}

View file

@ -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",
],
}

View file

@ -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>

View file

@ -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);
}
}