Merge changes I270ff43c,Ifc7eee24 am: cec294d1b4

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1730548

Change-Id: I435e69ba3fc71cf56a7bf8cc54b394831615fa57
This commit is contained in:
Mark Chien
2021-06-21 13:36:57 +00:00
committed by Automerger Merge Worker
8 changed files with 472 additions and 160 deletions

View File

@@ -193,4 +193,8 @@
state. -->
<!-- Config for showing upstream roaming notification. -->
<bool name="config_upstream_roaming_notification">false</bool>
<!-- Which USB function should be enabled when TETHERING_USB is requested. 0: RNDIS, 1: NCM.
-->
<integer translatable="false" name="config_tether_usb_functions">0</integer>
</resources>

View File

@@ -24,6 +24,7 @@
<item type="array" name="config_tether_wifi_p2p_regexs"/>
<item type="array" name="config_tether_bluetooth_regexs"/>
<item type="array" name="config_tether_dhcp_range"/>
<item type="integer" name="config_tether_usb_functions"/>
<!-- Use the BPF offload for tethering when the kernel has support. True by default.
If the device doesn't want to support tether BPF offload, this should be false.
Note that this setting could be overridden by device config.

View File

@@ -64,6 +64,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.isCellular;
@@ -77,6 +78,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.EthernetManager;
@@ -248,6 +250,7 @@ public class Tethering {
private InterfaceSet mCurrentUpstreamIfaceSet;
private boolean mRndisEnabled; // track the RNDIS function enabled state
private boolean mNcmEnabled; // track the NCM function enabled state
// True iff. WiFi tethering should be started when soft AP is ready.
private boolean mWifiTetherRequested;
private Network mTetherUpstream;
@@ -259,6 +262,7 @@ public class Tethering {
private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
private String mConfiguredEthernetIface;
private EthernetCallback mEthernetCallback;
private SettingsObserver mSettingsObserver;
public Tethering(TetheringDependencies deps) {
mLog.mark("Tethering.constructed");
@@ -310,6 +314,10 @@ public class Tethering {
mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
});
mSettingsObserver = new SettingsObserver(mHandler);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(TETHER_FORCE_USB_FUNCTIONS), false, mSettingsObserver);
mStateReceiver = new StateReceiver();
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -359,6 +367,28 @@ public class Tethering {
startStateMachineUpdaters();
}
private class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
mLog.i("OBSERVED Settings change");
final boolean isUsingNcm = mConfig.isUsingNcm();
updateConfiguration();
if (isUsingNcm != mConfig.isUsingNcm()) {
stopTetheringInternal(TETHERING_USB);
stopTetheringInternal(TETHERING_NCM);
}
}
}
@VisibleForTesting
ContentObserver getSettingsObserverForTest() {
return mSettingsObserver;
}
/**
* Start to register callbacks.
* Call this function when tethering is ready to handle callback events.
@@ -490,24 +520,35 @@ public class Tethering {
}
}
// This method needs to exist because TETHERING_BLUETOOTH and TETHERING_WIGIG can't use
// enableIpServing.
private void startOrStopIpServer(final String iface, boolean enabled) {
// TODO: do not listen to USB interface state changes. USB tethering is driven only by
// USB_ACTION broadcasts.
if (enabled) {
ensureIpServerStarted(iface);
} else {
ensureIpServerStopped(iface);
}
}
void interfaceStatusChanged(String iface, boolean up) {
// Never called directly: only called from interfaceLinkStateChanged.
// See NetlinkHandler.cpp: notifyInterfaceChanged.
if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
if (up) {
maybeTrackNewInterface(iface);
} else {
if (ifaceNameToType(iface) == TETHERING_BLUETOOTH
|| ifaceNameToType(iface) == TETHERING_WIGIG) {
stopTrackingInterface(iface);
} else {
// Ignore usb0 down after enabling RNDIS.
final int type = ifaceNameToType(iface);
if (!up && type != TETHERING_BLUETOOTH && type != TETHERING_WIGIG) {
// Ignore usb interface down after enabling RNDIS.
// We will handle disconnect in interfaceRemoved.
// Similarly, ignore interface down for WiFi. We monitor WiFi AP status
// through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent.
if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
return;
}
}
startOrStopIpServer(iface, up);
}
void interfaceLinkStateChanged(String iface, boolean up) {
@@ -535,12 +576,12 @@ public class Tethering {
void interfaceAdded(String iface) {
if (VDBG) Log.d(TAG, "interfaceAdded " + iface);
maybeTrackNewInterface(iface);
startOrStopIpServer(iface, true /* enabled */);
}
void interfaceRemoved(String iface) {
if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
stopTrackingInterface(iface);
startOrStopIpServer(iface, false /* enabled */);
}
void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
@@ -568,11 +609,14 @@ public class Tethering {
void stopTethering(int type) {
mHandler.post(() -> {
stopTetheringInternal(type);
});
}
void stopTetheringInternal(int type) {
mActiveTetheringRequests.remove(type);
enableTetheringInternal(type, false /* disabled */, null);
mEntitlementMgr.stopProvisioningIfNeeded(type);
});
}
/**
@@ -701,7 +745,7 @@ public class Tethering {
private void stopEthernetTethering() {
if (mConfiguredEthernetIface != null) {
stopTrackingInterface(mConfiguredEthernetIface);
ensureIpServerStopped(mConfiguredEthernetIface);
mConfiguredEthernetIface = null;
}
if (mEthernetCallback != null) {
@@ -718,8 +762,7 @@ public class Tethering {
// Ethernet callback arrived after Ethernet tethering stopped. Ignore.
return;
}
maybeTrackNewInterface(iface, TETHERING_ETHERNET);
changeInterfaceState(iface, getRequestedState(TETHERING_ETHERNET));
enableIpServing(TETHERING_ETHERNET, iface, getRequestedState(TETHERING_ETHERNET));
mConfiguredEthernetIface = iface;
}
@@ -851,6 +894,13 @@ public class Tethering {
: IpServer.STATE_TETHERED;
}
private int getRequestedUsbType(boolean forNcmFunction) {
// TETHERING_NCM is only used if the device does not use NCM for regular USB tethering.
if (forNcmFunction && !mConfig.isUsingNcm()) return TETHERING_NCM;
return TETHERING_USB;
}
// TODO: Figure out how to update for local hotspot mode interfaces.
private void sendTetherStateChangedBroadcast() {
if (!isTetheringSupported()) return;
@@ -877,12 +927,14 @@ public class Tethering {
} else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
localOnly.add(tetheringIface);
} else if (tetherState.lastState == IpServer.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
downstreamTypesMask |= (1 << TETHERING_USB);
} else if (cfg.isWifi(iface)) {
downstreamTypesMask |= (1 << TETHERING_WIFI);
} else if (cfg.isBluetooth(iface)) {
downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
switch (type) {
case TETHERING_USB:
case TETHERING_WIFI:
case TETHERING_BLUETOOTH:
downstreamTypesMask |= (1 << type);
break;
default:
// Do nothing.
}
tethered.add(tetheringIface);
}
@@ -987,8 +1039,8 @@ public class Tethering {
final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false);
mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s",
usbConnected, usbConfigured, rndisEnabled));
mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
usbConnected, usbConfigured, rndisEnabled, ncmEnabled));
// There are three types of ACTION_USB_STATE:
//
@@ -1005,19 +1057,18 @@ public class Tethering {
// functions are ready to use.
//
// For more explanation, see b/62552150 .
if (!usbConnected && mRndisEnabled) {
if (!usbConnected && (mRndisEnabled || mNcmEnabled)) {
// Turn off tethering if it was enabled and there is a disconnect.
tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
disableUsbIpServing(TETHERING_USB);
mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB);
} else if (usbConfigured && rndisEnabled) {
// Tether if rndis is enabled and usb is configured.
final int state = getRequestedState(TETHERING_USB);
tetherMatchingInterfaces(state, TETHERING_USB);
} else if (usbConnected && ncmEnabled) {
final int state = getRequestedState(TETHERING_NCM);
tetherMatchingInterfaces(state, TETHERING_NCM);
enableUsbIpServing(false /* isNcm */);
} else if (usbConfigured && ncmEnabled) {
enableUsbIpServing(true /* isNcm */);
}
mRndisEnabled = usbConfigured && rndisEnabled;
mNcmEnabled = usbConfigured && ncmEnabled;
}
private void handleWifiApAction(Intent intent) {
@@ -1164,6 +1215,11 @@ public class Tethering {
}
}
private void enableIpServing(int tetheringType, String ifname, int ipServingMode) {
ensureIpServerStarted(ifname, tetheringType);
changeInterfaceState(ifname, ipServingMode);
}
private void disableWifiIpServingCommon(int tetheringType, String ifname, int apState) {
mLog.log("Canceling WiFi tethering request -"
+ " type=" + tetheringType
@@ -1224,7 +1280,7 @@ public class Tethering {
}
if (!TextUtils.isEmpty(ifname)) {
maybeTrackNewInterface(ifname);
ensureIpServerStarted(ifname);
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(
@@ -1239,18 +1295,17 @@ public class Tethering {
// - handles both enabling and disabling serving states
// - only tethers the first matching interface in listInterfaces()
// order of a given type
private void tetherMatchingInterfaces(int requestedState, int interfaceType) {
if (VDBG) {
Log.d(TAG, "tetherMatchingInterfaces(" + requestedState + ", " + interfaceType + ")");
}
private void enableUsbIpServing(boolean isNcm) {
final int interfaceType = getRequestedUsbType(isNcm);
final int requestedState = getRequestedState(interfaceType);
String[] ifaces = null;
try {
ifaces = mNetd.interfaceGetList();
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Error listing Interfaces", e);
mLog.e("Cannot enableUsbIpServing due to error listing Interfaces" + e);
return;
}
String chosenIface = null;
if (ifaces != null) {
for (String iface : ifaces) {
@@ -1260,6 +1315,7 @@ public class Tethering {
}
}
}
if (chosenIface == null) {
Log.e(TAG, "could not find iface of type " + interfaceType);
return;
@@ -1268,6 +1324,33 @@ public class Tethering {
changeInterfaceState(chosenIface, requestedState);
}
private void disableUsbIpServing(int interfaceType) {
String[] ifaces = null;
try {
ifaces = mNetd.interfaceGetList();
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Cannot disableUsbIpServing due to error listing Interfaces" + e);
return;
}
String chosenIface = null;
if (ifaces != null) {
for (String iface : ifaces) {
if (ifaceNameToType(iface) == interfaceType) {
chosenIface = iface;
break;
}
}
}
if (chosenIface == null) {
Log.e(TAG, "could not find iface of type " + interfaceType);
return;
}
changeInterfaceState(chosenIface, IpServer.STATE_AVAILABLE);
}
private void changeInterfaceState(String ifname, int requestedState) {
final int result;
switch (requestedState) {
@@ -1320,13 +1403,21 @@ public class Tethering {
mLog.e("setUsbTethering: failed to get UsbManager!");
return TETHER_ERROR_SERVICE_UNAVAIL;
}
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
: UsbManager.FUNCTION_NONE);
final long usbFunction = mConfig.isUsingNcm()
? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_RNDIS;
usbManager.setCurrentFunctions(enable ? usbFunction : UsbManager.FUNCTION_NONE);
return TETHER_ERROR_NO_ERROR;
}
private int setNcmTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")");
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
// available.
if (mConfig.isUsingNcm()) return TETHER_ERROR_SERVICE_UNAVAIL;
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_NONE);
return TETHER_ERROR_NO_ERROR;
@@ -2437,7 +2528,7 @@ public class Tethering {
mTetherMainSM.sendMessage(which, state, 0, newLp);
}
private void maybeTrackNewInterface(final String iface) {
private void ensureIpServerStarted(final String iface) {
// If we don't care about this type of interface, ignore.
final int interfaceType = ifaceNameToType(iface);
if (interfaceType == TETHERING_INVALID) {
@@ -2457,17 +2548,17 @@ public class Tethering {
return;
}
maybeTrackNewInterface(iface, interfaceType);
ensureIpServerStarted(iface, interfaceType);
}
private void maybeTrackNewInterface(final String iface, int interfaceType) {
private void ensureIpServerStarted(final String iface, int interfaceType) {
// If we have already started a TISM for this interface, skip.
if (mTetherStates.containsKey(iface)) {
mLog.log("active iface (" + iface + ") reported as added, ignoring");
return;
}
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
mLog.log("adding IpServer for: " + iface);
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
@@ -2477,14 +2568,12 @@ public class Tethering {
tetherState.ipServer.start();
}
private void stopTrackingInterface(final String iface) {
private void ensureIpServerStopped(final String iface) {
final TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
mLog.log("attempting to remove unknown iface (" + iface + "), ignoring");
return;
}
if (tetherState == null) return;
tetherState.ipServer.stop();
mLog.log("removing TetheringInterfaceStateMachine for: " + iface);
mLog.log("removing IpServer for: " + iface);
mTetherStates.remove(iface);
}

View File

@@ -23,11 +23,13 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.TetheringConfigurationParcel;
import android.net.util.SharedLog;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -75,6 +77,12 @@ public class TetheringConfiguration {
private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
@VisibleForTesting
public static final int TETHER_USB_RNDIS_FUNCTION = 0;
@VisibleForTesting
public static final int TETHER_USB_NCM_FUNCTION = 1;
/**
* Override enabling BPF offload configuration for tethering.
*/
@@ -104,13 +112,20 @@ public class TetheringConfiguration {
* via resource overlays, and later noticed issues. To that end, it overrides
* config_tether_upstream_automatic when set to true.
*
* This flag is enabled if !=0 and less than the module APK version: see
* This flag is enabled if !=0 and less than the module APEX version: see
* {@link DeviceConfigUtils#isFeatureEnabled}. It is also ignored after R, as later devices
* should just set config_tether_upstream_automatic to true instead.
*/
public static final String TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION =
"tether_force_upstream_automatic_version";
/**
* Settings key to foce choosing usb functions for usb tethering.
*
* TODO: Remove this hard code string and make Settings#TETHER_FORCE_USB_FUNCTIONS as API.
*/
public static final String TETHER_FORCE_USB_FUNCTIONS =
"tether_force_usb_functions";
/**
* Default value that used to periodic polls tether offload stats from tethering offload HAL
* to make the data warnings work.
@@ -143,12 +158,17 @@ public class TetheringConfiguration {
private final boolean mEnableWifiP2pDedicatedIp;
private final boolean mEnableSelectAllPrefixRange;
private final int mUsbTetheringFunction;
protected final ContentResolver mContentResolver;
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
final SharedLog configLog = log.forSubComponent("config");
activeDataSubId = id;
Resources res = getResources(ctx, activeDataSubId);
mContentResolver = ctx.getContentResolver();
mUsbTetheringFunction = getUsbTetheringFunction(res);
tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs);
@@ -200,6 +220,11 @@ public class TetheringConfiguration {
configLog.log(toString());
}
/** Check whether using ncm for usb tethering */
public boolean isUsingNcm() {
return mUsbTetheringFunction == TETHER_USB_NCM_FUNCTION;
}
/** Check whether input interface belong to usb.*/
public boolean isUsb(String iface) {
return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
@@ -285,6 +310,9 @@ public class TetheringConfiguration {
pw.print("mEnableSelectAllPrefixRange: ");
pw.println(mEnableSelectAllPrefixRange);
pw.print("mUsbTetheringFunction: ");
pw.println(isUsingNcm() ? "NCM" : "RNDIS");
}
/** Returns the string representation of this object.*/
@@ -350,6 +378,26 @@ public class TetheringConfiguration {
return mEnableSelectAllPrefixRange;
}
private int getUsbTetheringFunction(Resources res) {
final int valueFromRes = getResourceInteger(res, R.integer.config_tether_usb_functions,
TETHER_USB_RNDIS_FUNCTION /* defaultValue */);
return getSettingsIntValue(TETHER_FORCE_USB_FUNCTIONS, valueFromRes);
}
private int getSettingsIntValue(final String name, final int defaultValue) {
final String value = getSettingsValue(name);
try {
return value != null ? Integer.parseInt(value) : defaultValue;
} catch (NumberFormatException e) {
return defaultValue;
}
}
@VisibleForTesting
protected String getSettingsValue(final String name) {
return Settings.Global.getString(mContentResolver, name);
}
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);

View File

@@ -226,7 +226,7 @@ public final class EntitlementManagerTest {
mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog,
mPermissionChangeCallback);
mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mEnMgr.setTetheringConfigurationFetcher(() -> {
return mConfig;
});
@@ -251,7 +251,7 @@ public final class EntitlementManagerTest {
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
}
@Test
@@ -265,7 +265,7 @@ public final class EntitlementManagerTest {
setupForRequiredProvisioning();
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(null);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
// Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
// Therefore provisioning still be required.
assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
@@ -275,7 +275,7 @@ public final class EntitlementManagerTest {
public void toleratesCarrierConfigMissing() {
setupForRequiredProvisioning();
when(mCarrierConfigManager.getConfig()).thenReturn(null);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
// We still have a provisioning app configured, so still require provisioning.
assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
}
@@ -293,11 +293,11 @@ public final class EntitlementManagerTest {
setupForRequiredProvisioning();
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(null);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(new String[] {"malformedApp"});
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2021 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.networkstack.tethering;
import android.content.Context;
import android.content.res.Resources;
import android.net.util.SharedLog;
/** FakeTetheringConfiguration is used to override static method for testing. */
public class FakeTetheringConfiguration extends TetheringConfiguration {
FakeTetheringConfiguration(Context ctx, SharedLog log, int id) {
super(ctx, log, id);
}
@Override
protected String getDeviceConfigProperty(final String name) {
return null;
}
@Override
protected boolean isFeatureEnabled(Context ctx, String featureVersionFlag) {
return false;
}
@Override
protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
return ctx.getResources();
}
@Override
protected String getSettingsValue(final String name) {
if (mContentResolver == null) return null;
return super.getSettingsValue(name);
}
}

View File

@@ -26,6 +26,9 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -35,6 +38,7 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -42,12 +46,15 @@ import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Build;
import android.provider.DeviceConfig;
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 com.android.net.module.util.DeviceConfigUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -78,6 +85,7 @@ public class TetheringConfigurationTest {
private static final String TEST_PACKAGE_NAME = "com.android.tethering.test";
private static final String APEX_NAME = "com.android.tethering";
private static final long TEST_PACKAGE_VERSION = 1234L;
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private TelephonyManager mTelephonyManager;
@Mock private Resources mResources;
@@ -88,6 +96,7 @@ public class TetheringConfigurationTest {
private boolean mHasTelephonyManager;
private boolean mEnableLegacyDhcpServer;
private MockitoSession mMockingSession;
private MockContentResolver mContentResolver;
private class MockTetheringConfiguration extends TetheringConfiguration {
MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
@@ -105,6 +114,11 @@ public class TetheringConfigurationTest {
super(base);
}
@Override
public ApplicationInfo getApplicationInfo() {
return mApplicationInfo;
}
@Override
public Resources getResources() {
return mResources;
@@ -153,7 +167,8 @@ public class TetheringConfigurationTest {
new String[0]);
when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[]{ "test_usb\\d" });
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn(
@@ -171,12 +186,20 @@ public class TetheringConfigurationTest {
mHasTelephonyManager = true;
mMockContext = new MockContext(mContext);
mEnableLegacyDhcpServer = false;
mContentResolver = new MockContentResolver(mMockContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
// Call {@link #clearSettingsProvider()} before and after using FakeSettingsProvider.
FakeSettingsProvider.clearSettingsProvider();
}
@After
public void tearDown() throws Exception {
mMockingSession.finishMocking();
DeviceConfigUtils.resetPackageVersionCacheForTest();
// Call {@link #clearSettingsProvider()} before and after using FakeSettingsProvider.
FakeSettingsProvider.clearSettingsProvider();
}
private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
@@ -539,4 +562,42 @@ public class TetheringConfigurationTest {
assertEquals(value, new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)
.chooseUpstreamAutomatically);
}
@Test
public void testUsbTetheringFunctions() throws Exception {
// Test default value. If both resource and settings is not configured, usingNcm is false.
assertIsUsingNcm(false /* usingNcm */);
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
TETHER_USB_NCM_FUNCTION);
assertIsUsingNcm(true /* usingNcm */);
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
TETHER_USB_RNDIS_FUNCTION);
assertIsUsingNcm(false /* usingNcm */);
setTetherForceUsbFunctions(TETHER_USB_RNDIS_FUNCTION);
assertIsUsingNcm(false /* usingNcm */);
setTetherForceUsbFunctions(TETHER_USB_NCM_FUNCTION);
assertIsUsingNcm(true /* usingNcm */);
// Test throws NumberFormatException.
setTetherForceUsbFunctions("WrongNumberFormat");
assertIsUsingNcm(false /* usingNcm */);
}
private void assertIsUsingNcm(boolean expected) {
final TetheringConfiguration cfg =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertEquals(expected, cfg.isUsingNcm());
}
private void setTetherForceUsbFunctions(final String value) {
Settings.Global.putString(mContentResolver, TETHER_FORCE_USB_FUNCTIONS, value);
}
private void setTetherForceUsbFunctions(final int value) {
setTetherForceUsbFunctions(Integer.toString(value));
}
}

View File

@@ -46,6 +46,7 @@ import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
@@ -65,6 +66,9 @@ import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH
import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST;
import static com.android.networkstack.tethering.Tethering.UserRestrictionActionListener;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES;
import static com.android.testutils.TestPermissionUtil.runAsShell;
@@ -106,6 +110,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.EthernetManager;
@@ -178,7 +183,6 @@ import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAge
import com.android.testutils.MiscAsserts;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -207,7 +211,7 @@ public class TetheringTest {
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
private static final String TEST_DUN_IFNAME = "test_dun0";
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_RNDIS_IFNAME = "test_rndis0";
private static final String TEST_WIFI_IFNAME = "test_wlan0";
private static final String TEST_WLAN_IFNAME = "test_wlan1";
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
@@ -217,6 +221,11 @@ public class TetheringTest {
private static final String TETHERING_NAME = "Tethering";
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
private static final String TEST_RNDIS_REGEX = "test_rndis\\d";
private static final String TEST_NCM_REGEX = "test_ncm\\d";
private static final String TEST_WIFI_REGEX = "test_wlan\\d";
private static final String TEST_P2P_REGEX = "test_p2p-p2p\\d-.*";
private static final String TEST_BT_REGEX = "test_pan\\d";
private static final int CELLULAR_NETID = 100;
private static final int WIFI_NETID = 101;
@@ -339,7 +348,7 @@ public class TetheringTest {
@Override
public InterfaceParams getInterfaceParams(String ifName) {
assertTrue("Non-mocked interface " + ifName,
ifName.equals(TEST_USB_IFNAME)
ifName.equals(TEST_RNDIS_IFNAME)
|| ifName.equals(TEST_WLAN_IFNAME)
|| ifName.equals(TEST_WIFI_IFNAME)
|| ifName.equals(TEST_MOBILE_IFNAME)
@@ -349,7 +358,7 @@ public class TetheringTest {
|| ifName.equals(TEST_ETH_IFNAME)
|| ifName.equals(TEST_BT_IFNAME));
final String[] ifaces = new String[] {
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_WIFI_IFNAME, TEST_MOBILE_IFNAME,
TEST_RNDIS_IFNAME, TEST_WLAN_IFNAME, TEST_WIFI_IFNAME, TEST_MOBILE_IFNAME,
TEST_DUN_IFNAME, TEST_P2P_IFNAME, TEST_NCM_IFNAME, TEST_ETH_IFNAME};
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
@@ -373,28 +382,6 @@ public class TetheringTest {
}
}
// MyTetheringConfiguration is used to override static method for testing.
private class MyTetheringConfiguration extends TetheringConfiguration {
MyTetheringConfiguration(Context ctx, SharedLog log, int id) {
super(ctx, log, id);
}
@Override
protected String getDeviceConfigProperty(final String name) {
return null;
}
@Override
protected boolean isFeatureEnabled(Context ctx, String featureVersionFlag) {
return false;
}
@Override
protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
return mResources;
}
}
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine mUpstreamNetworkMonitorSM;
ArrayList<IpServer> mIpv6CoordinatorNotifyList;
@@ -456,7 +443,7 @@ public class TetheringTest {
@Override
public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
int subId) {
mConfig = spy(new MyTetheringConfiguration(ctx, log, subId));
mConfig = spy(new FakeTetheringConfiguration(ctx, log, subId));
return mConfig;
}
@@ -585,18 +572,13 @@ public class TetheringTest {
new Network(DUN_NETID));
}
// See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and
// after use.
// See FakeSettingsProvider#clearSettingsProvider() that this also needs to be called before
// use.
@BeforeClass
public static void setupOnce() {
FakeSettingsProvider.clearSettingsProvider();
}
@AfterClass
public static void tearDownOnce() {
FakeSettingsProvider.clearSettingsProvider();
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -606,7 +588,7 @@ public class TetheringTest {
false);
when(mNetd.interfaceGetList())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_RNDIS_IFNAME, TEST_P2P_IFNAME,
TEST_NCM_IFNAME, TEST_ETH_IFNAME, TEST_BT_IFNAME});
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
mInterfaceConfiguration = new InterfaceConfigurationParcel();
@@ -659,17 +641,19 @@ public class TetheringTest {
supported ? 1 : 0);
when(mUserManager.hasUserRestriction(
UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported);
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION);
// Setup tetherable configuration.
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[] { "test_rndis\\d" });
.thenReturn(new String[] { TEST_RNDIS_REGEX});
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[] { "test_wlan\\d" });
.thenReturn(new String[] { TEST_WIFI_REGEX });
when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[] { "test_p2p-p2p\\d-.*" });
.thenReturn(new String[] { TEST_P2P_REGEX });
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[] { "test_pan\\d" });
.thenReturn(new String[] { TEST_BT_REGEX });
when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
.thenReturn(new String[] { "test_ncm\\d" });
.thenReturn(new String[] { TEST_NCM_REGEX });
when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
new int[] { TYPE_WIFI, TYPE_MOBILE_DUN });
when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
@@ -705,6 +689,7 @@ public class TetheringTest {
@After
public void tearDown() {
mServiceContext.unregisterReceiver(mBroadcastReceiver);
FakeSettingsProvider.clearSettingsProvider();
}
private void sendWifiApStateChanged(int state) {
@@ -750,16 +735,18 @@ public class TetheringTest {
mLooper.dispatchAll();
}
private void sendUsbBroadcast(boolean connected, boolean configured, boolean function,
int type) {
private boolean tetherUsbFunctionMatches(int function, int enabledType) {
return function == enabledType;
}
private void sendUsbBroadcast(boolean connected, boolean configured, int function) {
final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.putExtra(USB_CONNECTED, connected);
intent.putExtra(USB_CONFIGURED, configured);
if (type == TETHERING_USB) {
intent.putExtra(USB_FUNCTION_RNDIS, function);
} else {
intent.putExtra(USB_FUNCTION_NCM, function);
}
intent.putExtra(USB_FUNCTION_RNDIS,
tetherUsbFunctionMatches(TETHER_USB_RNDIS_FUNCTION, function));
intent.putExtra(USB_FUNCTION_NCM,
tetherUsbFunctionMatches(TETHER_USB_NCM_FUNCTION, function));
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
mLooper.dispatchAll();
}
@@ -834,11 +821,18 @@ public class TetheringTest {
final TetheringRequestParcel request = createTetheringRequestParcel(TETHERING_USB);
mTethering.startTethering(request, null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
assertEquals(1, mTethering.getActiveTetheringRequests().size());
assertEquals(request, mTethering.getActiveTetheringRequests().get(TETHERING_USB));
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
if (mTethering.getTetheringConfiguration().isUsingNcm()) {
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NCM);
mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
} else {
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
mTethering.interfaceStatusChanged(TEST_RNDIS_IFNAME, true);
}
}
@Test
@@ -851,7 +845,7 @@ public class TetheringTest {
verifyNoMoreInteractions(mNetd);
// Pretend we then receive USB configured broadcast.
sendUsbBroadcast(true, true, true, TETHERING_USB);
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
// Now we should see the start of tethering mechanics (in this case:
// tetherMatchingInterfaces() which starts by fetching all interfaces).
verify(mNetd, times(1)).interfaceGetList();
@@ -923,16 +917,13 @@ public class TetheringTest {
*/
private void sendIPv6TetherUpdates(UpstreamNetworkState upstreamState) {
// IPv6TetheringCoordinator must have been notified of downstream
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
eq(IpServer.STATE_TETHERED));
for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) {
UpstreamNetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIpv6Provisioned()
? ipv6OnlyState.linkProperties
: null);
break;
}
mLooper.dispatchAll();
}
@@ -940,7 +931,18 @@ public class TetheringTest {
private void runUsbTethering(UpstreamNetworkState upstreamState) {
initTetheringUpstream(upstreamState);
prepareUsbTethering();
sendUsbBroadcast(true, true, true, TETHERING_USB);
if (mTethering.getTetheringConfiguration().isUsingNcm()) {
sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
verify(mIPv6TetheringCoordinator).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_NCM_IFNAME)),
eq(IpServer.STATE_TETHERED));
} else {
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
verify(mIPv6TetheringCoordinator).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_RNDIS_IFNAME)),
eq(IpServer.STATE_TETHERED));
}
}
private void assertSetIfaceToDadProxy(final int numOfCalls, final String ifaceName) {
@@ -956,8 +958,8 @@ public class TetheringTest {
UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
assertSetIfaceToDadProxy(0 /* numOfCalls */, "" /* ifaceName */);
@@ -983,8 +985,8 @@ public class TetheringTest {
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
// TODO: add interfaceParams to compare in verify.
@@ -998,8 +1000,8 @@ public class TetheringTest {
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
runUsbTethering(upstreamState);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
@@ -1015,12 +1017,13 @@ public class TetheringTest {
UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState();
runUsbTethering(upstreamState);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME,
TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
@@ -1030,14 +1033,20 @@ public class TetheringTest {
@Test
public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[] {TEST_NCM_REGEX});
sendConfigurationChanged();
// Setup IPv6
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
// Then 464xlat comes up
upstreamState = buildMobile464xlatUpstreamState();
@@ -1052,11 +1061,12 @@ public class TetheringTest {
mLooper.dispatchAll();
// Forwarding is added for 464xlat
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME,
TEST_XLAT_MOBILE_IFNAME);
// Forwarding was not re-added for v6 (still times(1))
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
// DHCP not restarted on downstream (still times(1))
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
@@ -1093,7 +1103,7 @@ public class TetheringTest {
// Start USB tethering with no current upstream.
prepareUsbTethering();
sendUsbBroadcast(true, true, true, TETHERING_USB);
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
inOrder.verify(mUpstreamNetworkMonitor).startObserveAllNetworks();
inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
@@ -1308,7 +1318,7 @@ public class TetheringTest {
// Start USB tethering with no current upstream.
prepareUsbTethering();
sendUsbBroadcast(true, true, true, TETHERING_USB);
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
inOrder.verify(mUpstreamNetworkMonitor).startObserveAllNetworks();
inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
ArgumentCaptor<NetworkCallback> captor = ArgumentCaptor.forClass(NetworkCallback.class);
@@ -1342,7 +1352,7 @@ public class TetheringTest {
private void runNcmTethering() {
prepareNcmTethering();
sendUsbBroadcast(true, true, true, TETHERING_NCM);
sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
}
@Test
@@ -1622,7 +1632,7 @@ public class TetheringTest {
// Start usb tethering and check that usb interface is tethered.
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
assertTrue(mTethering.isTetheringActive());
assertEquals(0, mTethering.getActiveTetheringRequests().size());
@@ -1862,7 +1872,7 @@ public class TetheringTest {
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
runStopUSBTethering();
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
reset(mUsbManager);
reset(mUsbManager, mIPv6TetheringCoordinator);
// 2. Offload fail if no OffloadControl.
initOffloadConfiguration(true /* offloadConfig */, false /* offloadControl */,
0 /* defaultDisabled */);
@@ -1870,7 +1880,7 @@ public class TetheringTest {
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
runStopUSBTethering();
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
reset(mUsbManager);
reset(mUsbManager, mIPv6TetheringCoordinator);
// 3. Offload fail if disabled by settings.
initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */,
1 /* defaultDisabled */);
@@ -1883,8 +1893,10 @@ public class TetheringTest {
private void runStopUSBTethering() {
mTethering.stopTethering(TETHERING_USB);
mLooper.dispatchAll();
mTethering.interfaceRemoved(TEST_USB_IFNAME);
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
sendUsbBroadcast(true, true, -1 /* function */);
mLooper.dispatchAll();
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
}
private void initOffloadConfiguration(final boolean offloadConfig,
@@ -2059,29 +2071,29 @@ public class TetheringTest {
// Start Tethering.
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
// Data saver is ON.
setDataSaverEnabled(true);
// Verify that tethering should be disabled.
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
mTethering.interfaceRemoved(TEST_USB_IFNAME);
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
mLooper.dispatchAll();
assertEquals(mTethering.getTetheredIfaces(), new String[0]);
reset(mUsbManager);
reset(mUsbManager, mIPv6TetheringCoordinator);
runUsbTethering(upstreamState);
// Verify that user can start tethering again without turning OFF data saver.
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
// If data saver is keep ON with change event, tethering should not be OFF this time.
setDataSaverEnabled(true);
verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
// If data saver is turned OFF, it should not change tethering.
setDataSaverEnabled(false);
verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
}
private static <T> void assertContains(Collection<T> collection, T element) {
@@ -2141,8 +2153,8 @@ public class TetheringTest {
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
// Expect that when USB comes up, the DHCP server is configured with the requested address.
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
sendUsbBroadcast(true, true, true, TETHERING_USB);
mTethering.interfaceStatusChanged(TEST_RNDIS_IFNAME, true);
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
@@ -2150,6 +2162,12 @@ public class TetheringTest {
@Test
public void testRequestStaticIp() throws Exception {
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[] {TEST_NCM_REGEX});
sendConfigurationChanged();
final LinkAddress serverLinkAddr = new LinkAddress("192.168.0.123/24");
final LinkAddress clientLinkAddr = new LinkAddress("192.168.0.42/24");
final String serverAddr = "192.168.0.123";
@@ -2159,9 +2177,9 @@ public class TetheringTest {
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
sendUsbBroadcast(true, true, true, TETHERING_USB);
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(),
any());
@@ -2328,7 +2346,7 @@ public class TetheringTest {
wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI);
// verify turn off usb tethering
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
mTethering.interfaceRemoved(TEST_USB_IFNAME);
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
mLooper.dispatchAll();
// verify restart usb tethering
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
@@ -2369,7 +2387,7 @@ public class TetheringTest {
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
// verify turn off ethernet tethering
verify(mockRequest).release();
mTethering.interfaceRemoved(TEST_USB_IFNAME);
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
ethCallback.onUnavailable();
mLooper.dispatchAll();
// verify restart usb tethering
@@ -2382,14 +2400,15 @@ public class TetheringTest {
reset(mUsbManager, mEm);
when(mNetd.interfaceGetList())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_RNDIS_IFNAME, TEST_P2P_IFNAME,
TEST_NCM_IFNAME, TEST_ETH_IFNAME});
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
sendUsbBroadcast(true, true, true, TETHERING_USB);
assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_USB_IFNAME);
mTethering.interfaceStatusChanged(TEST_RNDIS_IFNAME, true);
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_RNDIS_IFNAME);
assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_ETH_IFNAME);
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(TEST_USB_IFNAME));
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(
TEST_RNDIS_IFNAME));
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(TEST_ETH_IFNAME));
}
@@ -2580,6 +2599,46 @@ public class TetheringTest {
reset(mBluetoothAdapter, mBluetoothPan);
}
@Test
public void testUsbTetheringWithNcmFunction() throws Exception {
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[] {TEST_NCM_REGEX});
sendConfigurationChanged();
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
// available.
final ResultListener ncmResult = new ResultListener(TETHER_ERROR_SERVICE_UNAVAIL);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), ncmResult);
mLooper.dispatchAll();
ncmResult.assertHasResult();
final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
runUsbTethering(upstreamState);
verify(mNetd).interfaceGetList();
verify(mNetd).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon).start();
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
any(), any());
sendIPv6TetherUpdates(upstreamState);
assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
verify(mNetd).tetherApplyDnsInterfaces();
Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS,
TETHER_USB_RNDIS_FUNCTION);
final ContentObserver observer = mTethering.getSettingsObserverForTest();
observer.onChange(false /* selfChange */);
mLooper.dispatchAll();
// stop TETHERING_USB and TETHERING_NCM
verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
mTethering.interfaceRemoved(TEST_NCM_IFNAME);
sendUsbBroadcast(true, true, -1 /* function */);
}
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}