Merge changes I270ff43c,Ifc7eee24 am: cec294d1b4 am: 82b26e0d1f
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1730548
Change-Id: I382f551487f70865761b4dcd0dcdcdb425538f3e
diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml
index 4391006..0412a49 100644
--- a/Tethering/res/values/config.xml
+++ b/Tethering/res/values/config.xml
@@ -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>
diff --git a/Tethering/res/values/overlayable.xml b/Tethering/res/values/overlayable.xml
index 0ee7a99..91fbd7d 100644
--- a/Tethering/res/values/overlayable.xml
+++ b/Tethering/res/values/overlayable.xml
@@ -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.
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index e4fbd71..b52ec86 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -64,6 +64,7 @@
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.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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ // 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.
- // 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);
- }
+
+ 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 @@
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,12 +609,15 @@
void stopTethering(int type) {
mHandler.post(() -> {
- mActiveTetheringRequests.remove(type);
-
- enableTetheringInternal(type, false /* disabled */, null);
- mEntitlementMgr.stopProvisioningIfNeeded(type);
+ stopTetheringInternal(type);
});
}
+ void stopTetheringInternal(int type) {
+ mActiveTetheringRequests.remove(type);
+
+ enableTetheringInternal(type, false /* disabled */, null);
+ mEntitlementMgr.stopProvisioningIfNeeded(type);
+ }
/**
* Enables or disables tethering for the given type. If provisioning is required, it will
@@ -701,7 +745,7 @@
private void stopEthernetTethering() {
if (mConfiguredEthernetIface != null) {
- stopTrackingInterface(mConfiguredEthernetIface);
+ ensureIpServerStopped(mConfiguredEthernetIface);
mConfiguredEthernetIface = null;
}
if (mEthernetCallback != null) {
@@ -718,8 +762,7 @@
// 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 @@
: 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 @@
} 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 @@
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 @@
// 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 @@
}
}
+ 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 @@
}
if (!TextUtils.isEmpty(ifname)) {
- maybeTrackNewInterface(ifname);
+ ensureIpServerStarted(ifname);
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(
@@ -1239,18 +1295,17 @@
// - 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 @@
}
}
}
+
if (chosenIface == null) {
Log.e(TAG, "could not find iface of type " + interfaceType);
return;
@@ -1268,6 +1324,33 @@
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 @@
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 @@
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 @@
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 @@
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);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 2beeeb8..31fcea4 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -23,11 +23,13 @@
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 @@
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,7 +112,7 @@
* 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.
*/
@@ -112,6 +120,13 @@
"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 @@
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 @@
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 @@
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 @@
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);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 5ae4b43..442be1e 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -226,7 +226,7 @@
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 @@
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 @@
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 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 @@
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));
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
new file mode 100644
index 0000000..ac5c59d
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
@@ -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);
+ }
+}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index a6433a6..0f940d8 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -26,6 +26,9 @@
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.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.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 @@
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 @@
private boolean mHasTelephonyManager;
private boolean mEnableLegacyDhcpServer;
private MockitoSession mMockingSession;
+ private MockContentResolver mContentResolver;
private class MockTetheringConfiguration extends TetheringConfiguration {
MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
@@ -106,6 +115,11 @@
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ return mApplicationInfo;
+ }
+
+ @Override
public Resources getResources() {
return mResources;
}
@@ -153,7 +167,8 @@
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 @@
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 @@
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));
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 83f1322..af28dd7 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -46,6 +46,7 @@
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;
@@ -68,6 +69,9 @@
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;
@@ -109,6 +113,7 @@
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;
@@ -181,7 +186,6 @@
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;
@@ -210,7 +214,7 @@
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";
@@ -220,6 +224,11 @@
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;
@@ -342,7 +351,7 @@
@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)
@@ -352,7 +361,7 @@
|| 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);
@@ -376,28 +385,6 @@
}
}
- // 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;
@@ -459,7 +446,7 @@
@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;
}
@@ -588,18 +575,13 @@
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);
@@ -609,7 +591,7 @@
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();
@@ -662,17 +644,19 @@
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);
@@ -708,6 +692,7 @@
@After
public void tearDown() {
mServiceContext.unregisterReceiver(mBroadcastReceiver);
+ FakeSettingsProvider.clearSettingsProvider();
}
private void sendWifiApStateChanged(int state) {
@@ -753,16 +738,18 @@
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();
}
@@ -844,11 +831,18 @@
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
@@ -861,7 +855,7 @@
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();
@@ -933,16 +927,13 @@
*/
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();
}
@@ -950,7 +941,18 @@
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) {
@@ -966,8 +968,8 @@
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 */);
@@ -993,8 +995,8 @@
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.
@@ -1008,8 +1010,8 @@
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());
@@ -1025,12 +1027,13 @@
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 */);
@@ -1040,14 +1043,20 @@
@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();
@@ -1062,11 +1071,12 @@
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());
@@ -1103,7 +1113,7 @@
// 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);
@@ -1318,7 +1328,7 @@
// 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);
@@ -1352,7 +1362,7 @@
private void runNcmTethering() {
prepareNcmTethering();
- sendUsbBroadcast(true, true, true, TETHERING_NCM);
+ sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
}
@Test
@@ -1632,7 +1642,7 @@
// 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());
@@ -1872,7 +1882,7 @@
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 */, OFFLOAD_HAL_VERSION_NONE,
0 /* defaultDisabled */);
@@ -1880,7 +1890,7 @@
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 */, OFFLOAD_HAL_VERSION_1_0,
1 /* defaultDisabled */);
@@ -1893,8 +1903,10 @@
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,
@@ -2070,29 +2082,29 @@
// 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) {
@@ -2152,8 +2164,8 @@
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)));
@@ -2161,6 +2173,12 @@
@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";
@@ -2170,9 +2188,9 @@
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());
@@ -2339,7 +2357,7 @@
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);
@@ -2380,7 +2398,7 @@
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
@@ -2393,14 +2411,15 @@
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));
}
@@ -2591,6 +2610,46 @@
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.
}