[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:
parent
932da868a5
commit
43e97e01b7
5 changed files with 785 additions and 0 deletions
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
47
Tethering/tests/unit/Android.bp
Normal file
47
Tethering/tests/unit/Android.bp
Normal 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",
|
||||
],
|
||||
}
|
||||
26
Tethering/tests/unit/AndroidManifest.xml
Normal file
26
Tethering/tests/unit/AndroidManifest.xml
Normal 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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue