Snap for 7482982 from 3750a9f12e7ee07aff3b04c1f077a668507c5981 to sc-release
Change-Id: I539f13df79aff92c7a9f283a991d685885293407
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/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 485eec8..69471a1 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -191,7 +191,7 @@
* check even tethering is not active yet.
*/
public void stop() {
- releaseMobileNetworkRequest();
+ setTryCell(false);
releaseCallback(mListenAllCallback);
mListenAllCallback = null;
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 c636384..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,37 +385,10 @@
}
}
- // 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;
- public void reset() {
- mUpstreamNetworkMonitorSM = null;
- mIpv6CoordinatorNotifyList = null;
- }
-
@Override
public BpfCoordinator getBpfCoordinator(
BpfCoordinator.Dependencies deps) {
@@ -464,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;
}
@@ -593,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);
@@ -614,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();
@@ -667,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);
@@ -689,7 +668,6 @@
}
private Tethering makeTethering() {
- mTetheringDependencies.reset();
return new Tethering(mTetheringDependencies);
}
@@ -714,6 +692,7 @@
@After
public void tearDown() {
mServiceContext.unregisterReceiver(mBroadcastReceiver);
+ FakeSettingsProvider.clearSettingsProvider();
}
private void sendWifiApStateChanged(int state) {
@@ -759,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();
}
@@ -850,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
@@ -867,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();
@@ -939,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();
}
@@ -956,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) {
@@ -972,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 */);
@@ -999,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.
@@ -1014,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());
@@ -1031,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 */);
@@ -1046,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();
@@ -1068,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());
@@ -1095,6 +1099,11 @@
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
}
+ private void verifyDisableTryCellWhenTetheringStop(InOrder inOrder) {
+ runStopUSBTethering();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
+ }
+
private void upstreamSelectionTestCommon(final boolean automatic, InOrder inOrder,
TestNetworkAgent mobile, TestNetworkAgent wifi) throws Exception {
// Enable automatic upstream selection.
@@ -1104,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);
@@ -1118,6 +1127,7 @@
wifi.fakeConnect();
mCm.makeDefaultNetwork(wifi, BROADCAST_FIRST);
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
}
@@ -1140,6 +1150,7 @@
mCm.makeDefaultNetwork(wifi, BROADCAST_FIRST, doDispatchAll);
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
@@ -1148,6 +1159,7 @@
mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST);
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST, doDispatchAll);
@@ -1166,6 +1178,7 @@
mLooper.dispatchAll();
mobile.fakeDisconnect();
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
mobile = new TestNetworkAgent(mCm, buildMobile464xlatUpstreamState());
@@ -1183,6 +1196,7 @@
mCm.makeDefaultNetwork(null, CALLBACKS_FIRST, doDispatchAll);
mobile.fakeDisconnect();
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
@@ -1198,6 +1212,8 @@
mobile.fakeDisconnect();
mobile.sendLinkProperties();
mLooper.dispatchAll();
+
+ verifyDisableTryCellWhenTetheringStop(inOrder);
}
@Test
@@ -1220,6 +1236,10 @@
mLooper.dispatchAll();
mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST, null);
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
+ inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
+
+ verifyDisableTryCellWhenTetheringStop(inOrder);
}
@Test
@@ -1235,6 +1255,7 @@
// automatic mode would request dun again and choose it as upstream.
mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
ArgumentCaptor<NetworkCallback> captor = ArgumentCaptor.forClass(NetworkCallback.class);
inOrder.verify(mCm).requestNetwork(any(), eq(0), eq(TYPE_MOBILE_DUN), any(), any());
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
@@ -1246,11 +1267,14 @@
// Lose and regain upstream again.
dun.fakeDisconnect(CALLBACKS_FIRST, doDispatchAll);
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
inOrder.verify(mCm, never()).unregisterNetworkCallback(any(NetworkCallback.class));
dun.fakeConnect(CALLBACKS_FIRST, doDispatchAll);
mLooper.dispatchAll();
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(dun.networkId);
+
+ verifyDisableTryCellWhenTetheringStop(inOrder);
}
@Test
@@ -1266,6 +1290,7 @@
// list).
mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
mLooper.dispatchAll();
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
// BUG: when wifi disconnect, the dun request would not be filed again because wifi is
// no longer be default network which do not have CONNECTIVIY_ACTION broadcast.
@@ -1287,8 +1312,11 @@
mLooper.dispatchAll();
// BUG: dun has higher priority than wifi but tethering don't file dun request because
// current upstream is wifi.
+ inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
inOrder.verify(mCm, never()).requestNetwork(any(), eq(0), eq(TYPE_MOBILE_DUN), any(),
any());
+
+ verifyDisableTryCellWhenTetheringStop(inOrder);
}
private void chooseDunUpstreamTestCommon(final boolean automatic, InOrder inOrder,
@@ -1300,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);
@@ -1334,7 +1362,7 @@
private void runNcmTethering() {
prepareNcmTethering();
- sendUsbBroadcast(true, true, true, TETHERING_NCM);
+ sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
}
@Test
@@ -1614,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());
@@ -1854,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 */);
@@ -1862,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 */);
@@ -1875,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,
@@ -2052,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) {
@@ -2134,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)));
@@ -2143,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";
@@ -2152,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());
@@ -2321,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);
@@ -2362,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
@@ -2375,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));
}
@@ -2573,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.
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityFrameworkInitializerTest.kt b/tests/cts/net/src/android/net/cts/ConnectivityFrameworkInitializerTest.kt
new file mode 100644
index 0000000..d687eaa
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/ConnectivityFrameworkInitializerTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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 android.net.cts
+
+import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.net.ConnectivityDiagnosticsManager
+import android.net.ConnectivityFrameworkInitializer
+import android.net.ConnectivityManager
+import android.net.TestNetworkManager
+import android.os.Build
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.runAsShell
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertNotNull
+
+@RunWith(DevSdkIgnoreRunner::class)
+// ConnectivityFrameworkInitializer was added in S
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+class ConnectivityFrameworkInitializerTest {
+ @Test
+ fun testServicesRegistered() {
+ val ctx = InstrumentationRegistry.getInstrumentation().context as android.content.Context
+ assertNotNull(ctx.getSystemService(ConnectivityManager::class.java),
+ "ConnectivityManager not registered")
+ assertNotNull(ctx.getSystemService(ConnectivityDiagnosticsManager::class.java),
+ "ConnectivityDiagnosticsManager not registered")
+
+ runAsShell(MANAGE_TEST_NETWORKS) {
+ assertNotNull(ctx.getSystemService(TestNetworkManager::class.java),
+ "TestNetworkManager not registered")
+ }
+ // Do not test for DnsResolverServiceManager as it is internal to Connectivity, and
+ // CTS does not have permission to get it anyway.
+ }
+
+ // registerServiceWrappers can only be called during initialization and should throw otherwise
+ @Test(expected = IllegalStateException::class)
+ fun testThrows() {
+ ConnectivityFrameworkInitializer.registerServiceWrappers()
+ }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 34f3cc9..d649518 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -66,11 +66,13 @@
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.AF_UNSPEC;
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
import static com.android.testutils.MiscAsserts.assertThrows;
+import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
@@ -113,6 +115,7 @@
import android.net.NetworkSpecifier;
import android.net.NetworkStateSnapshot;
import android.net.NetworkUtils;
+import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.SocketKeepalive;
import android.net.TelephonyNetworkSpecifier;
@@ -157,6 +160,7 @@
import com.android.testutils.DevSdkIgnoreRuleKt;
import com.android.testutils.RecorderCallback.CallbackEntry;
import com.android.testutils.SkipPresubmit;
+import com.android.testutils.TestNetworkTracker;
import com.android.testutils.TestableNetworkCallback;
import junit.framework.AssertionFailedError;
@@ -193,6 +197,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -520,13 +525,8 @@
@Test
@SkipPresubmit(reason = "Virtual devices use a single internet connection for all networks")
public void testOpenConnection() throws Exception {
- boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
- && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
- if (!canRunTest) {
- Log.i(TAG,"testOpenConnection cannot execute unless device supports both WiFi "
- + "and a cellular connection");
- return;
- }
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
Network wifiNetwork = mCtsNetUtils.connectToWifi();
Network cellNetwork = mCtsNetUtils.connectToCell();
@@ -713,10 +713,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testRegisterNetworkCallback() {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
- return;
- }
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
// We will register for a WIFI network being available or lost.
final TestNetworkCallback callback = new TestNetworkCallback();
@@ -787,10 +784,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testRegisterNetworkCallback_withPendingIntent() {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
- return;
- }
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
// Create a ConnectivityActionReceiver that has an IntentFilter for our locally defined
// action, NETWORK_CALLBACK_ACTION.
@@ -961,7 +955,8 @@
}
}
- private void waitForActiveNetworkMetered(int targetTransportType, boolean requestedMeteredness)
+ private void waitForActiveNetworkMetered(final int targetTransportType,
+ final boolean requestedMeteredness, final boolean useSystemDefault)
throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final NetworkCallback networkCallback = new NetworkCallback() {
@@ -975,17 +970,36 @@
}
}
};
- // Registering a callback here guarantees onCapabilitiesChanged is called immediately
- // with the current setting. Therefore, if the setting has already been changed,
- // this method will return right away, and if not it will wait for the setting to change.
- mCm.registerDefaultNetworkCallback(networkCallback);
- // Changing meteredness on wifi involves reconnecting, which can take several seconds
- // (involves re-associating, DHCP...).
- if (!latch.await(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- fail("Timed out waiting for active network metered status to change to "
- + requestedMeteredness + " ; network = " + mCm.getActiveNetwork());
+
+ try {
+ // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+ // with the current setting. Therefore, if the setting has already been changed,
+ // this method will return right away, and if not, it'll wait for the setting to change.
+ if (useSystemDefault) {
+ runWithShellPermissionIdentity(() ->
+ mCmShim.registerSystemDefaultNetworkCallback(networkCallback,
+ new Handler(Looper.getMainLooper())),
+ NETWORK_SETTINGS);
+ } else {
+ mCm.registerDefaultNetworkCallback(networkCallback);
+ }
+
+ // Changing meteredness on wifi involves reconnecting, which can take several seconds
+ // (involves re-associating, DHCP...).
+ if (!latch.await(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for active network metered status to change to "
+ + requestedMeteredness + " ; network = " + mCm.getActiveNetwork());
+ }
+ } finally {
+ mCm.unregisterNetworkCallback(networkCallback);
}
- mCm.unregisterNetworkCallback(networkCallback);
+ }
+
+ private void setWifiMeteredStatusAndWait(String ssid, boolean isMetered) throws Exception {
+ setWifiMeteredStatus(ssid, Boolean.toString(isMetered) /* metered */);
+ waitForActiveNetworkMetered(TRANSPORT_WIFI,
+ isMetered /* requestedMeteredness */,
+ true /* useSystemDefault */);
}
private void assertMultipathPreferenceIsEventually(Network network, int oldValue,
@@ -1047,10 +1061,9 @@
int newMeteredPreference = findNextPrefValue(resolver);
Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
Integer.toString(newMeteredPreference));
- setWifiMeteredStatus(ssid, "true");
- waitForActiveNetworkMetered(TRANSPORT_WIFI, true);
// Wifi meterness changes from unmetered to metered will disconnect and reconnect since
// R.
+ setWifiMeteredStatusAndWait(ssid, true);
final Network network = mCtsNetUtils.ensureWifiConnected();
assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID()));
assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
@@ -1067,9 +1080,8 @@
assertMultipathPreferenceIsEventually(network,
oldMeteredPreference, newMeteredPreference);
- setWifiMeteredStatus(ssid, "false");
// No disconnect from unmetered to metered.
- waitForActiveNetworkMetered(TRANSPORT_WIFI, false);
+ setWifiMeteredStatusAndWait(ssid, false);
assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
NET_CAPABILITY_NOT_METERED), true);
assertMultipathPreferenceIsEventually(network, newMeteredPreference,
@@ -1259,11 +1271,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testKeepaliveWifiUnsupported() throws Exception {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testKeepaliveUnsupported cannot execute unless device"
- + " supports WiFi");
- return;
- }
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
final Network network = mCtsNetUtils.ensureWifiConnected();
if (getSupportedKeepalivesForNet(network) != 0) return;
@@ -1280,10 +1288,7 @@
@Test
@SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
public void testCreateTcpKeepalive() throws Exception {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testCreateTcpKeepalive cannot execute unless device supports WiFi");
- return;
- }
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
final Network network = mCtsNetUtils.ensureWifiConnected();
if (getSupportedKeepalivesForNet(network) == 0) return;
@@ -1490,11 +1495,7 @@
@Test
@SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
public void testSocketKeepaliveLimitWifi() throws Exception {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testSocketKeepaliveLimitWifi cannot execute unless device"
- + " supports WiFi");
- return;
- }
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
final Network network = mCtsNetUtils.ensureWifiConnected();
final int supported = getSupportedKeepalivesForNet(network);
@@ -1590,11 +1591,7 @@
@Test
@SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
public void testSocketKeepaliveUnprivileged() throws Exception {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testSocketKeepaliveUnprivileged cannot execute unless device"
- + " supports WiFi");
- return;
- }
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
final Network network = mCtsNetUtils.ensureWifiConnected();
final int supported = getSupportedKeepalivesForNet(network);
@@ -1741,6 +1738,24 @@
c -> c instanceof CallbackEntry.Available);
}
+ private void waitForAvailable(
+ @NonNull final TestableNetworkCallback cb, final int expectedTransport) {
+ cb.eventuallyExpect(
+ CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(entry.getNetwork());
+ return nc.hasTransport(expectedTransport);
+ }
+ );
+ }
+
+ private void waitForAvailable(
+ @NonNull final TestableNetworkCallback cb, @NonNull final Network expectedNetwork) {
+ cb.expectAvailableCallbacks(expectedNetwork, false /* suspended */,
+ true /* validated */,
+ false /* blocked */, NETWORK_CALLBACK_TIMEOUT_MS);
+ }
+
private void waitForLost(@NonNull final TestableNetworkCallback cb) {
cb.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
c -> c instanceof CallbackEntry.Lost);
@@ -2141,4 +2156,175 @@
startTetheringCallback.verifyTetheringStarted();
callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
}
+
+ /**
+ * Verify that per-app OEM network preference functions as expected for network preference TEST.
+ * For specified apps, validate networks are prioritized in order: unmetered, TEST transport,
+ * default network.
+ */
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
+ public void testSetOemNetworkPreferenceForTestPref() throws Exception {
+ // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
+ // shims, and @IgnoreUpTo does not check that.
+ assumeTrue(TestUtils.shouldTestSApis());
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
+
+ final TestNetworkTracker tnt = callWithShellPermissionIdentity(
+ () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
+ final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
+ final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
+
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ final NetworkCapabilities wifiNetworkCapabilities = callWithShellPermissionIdentity(
+ () -> mCm.getNetworkCapabilities(wifiNetwork));
+ final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
+ final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
+
+ try {
+ // This network will be used for unmetered.
+ setWifiMeteredStatusAndWait(ssid, false /* isMetered */);
+
+ setOemNetworkPreferenceForMyPackage(OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST);
+ registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+
+ // Validate that an unmetered network is used over other networks.
+ waitForAvailable(defaultCallback, wifiNetwork);
+ waitForAvailable(systemDefaultCallback, wifiNetwork);
+
+ // Validate when setting unmetered to metered, unmetered is lost and replaced by the
+ // network with the TEST transport.
+ setWifiMeteredStatusAndWait(ssid, true /* isMetered */);
+ defaultCallback.expectCallback(CallbackEntry.LOST, wifiNetwork,
+ NETWORK_CALLBACK_TIMEOUT_MS);
+ waitForAvailable(defaultCallback, tnt.getNetwork());
+ // Depending on if this device has cellular connectivity or not, multiple available
+ // callbacks may be received. Eventually, metered Wi-Fi should be the final available
+ // callback in any case therefore confirm its receipt before continuing to assure the
+ // system is in the expected state.
+ waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
+ } finally {
+ // Validate that removing the test network will fallback to the default network.
+ runWithShellPermissionIdentity(tnt::teardown);
+ defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
+ NETWORK_CALLBACK_TIMEOUT_MS);
+ waitForAvailable(defaultCallback);
+
+ setWifiMeteredStatusAndWait(ssid, oldMeteredValue);
+
+ // Cleanup any prior test state from setOemNetworkPreference
+ clearOemNetworkPreference();
+ unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+ }
+ }
+
+ /**
+ * Verify that per-app OEM network preference functions as expected for network pref TEST_ONLY.
+ * For specified apps, validate that only TEST transport type networks are used.
+ */
+ @Test
+ public void testSetOemNetworkPreferenceForTestOnlyPref() throws Exception {
+ // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
+ // shims, and @IgnoreUpTo does not check that.
+ assumeTrue(TestUtils.shouldTestSApis());
+
+ final TestNetworkTracker tnt = callWithShellPermissionIdentity(
+ () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
+ final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
+ final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
+
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+
+ try {
+ setOemNetworkPreferenceForMyPackage(
+ OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
+ registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+ waitForAvailable(defaultCallback, tnt.getNetwork());
+ waitForAvailable(systemDefaultCallback, wifiNetwork);
+ } finally {
+ runWithShellPermissionIdentity(tnt::teardown);
+ defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
+ NETWORK_CALLBACK_TIMEOUT_MS);
+
+ // This network preference should only ever use the test network therefore available
+ // should not trigger when the test network goes down (e.g. switch to cellular).
+ defaultCallback.assertNoCallback();
+ // The system default should still be connected to Wi-fi
+ assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+
+ // Cleanup any prior test state from setOemNetworkPreference
+ clearOemNetworkPreference();
+
+ // The default (non-test) network should be available as the network pref was cleared.
+ waitForAvailable(defaultCallback);
+ unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+ }
+ }
+
+ private void unregisterTestOemNetworkPreferenceCallbacks(
+ @NonNull final TestableNetworkCallback defaultCallback,
+ @NonNull final TestableNetworkCallback systemDefaultCallback) {
+ mCm.unregisterNetworkCallback(defaultCallback);
+ mCm.unregisterNetworkCallback(systemDefaultCallback);
+ }
+
+ private void registerTestOemNetworkPreferenceCallbacks(
+ @NonNull final TestableNetworkCallback defaultCallback,
+ @NonNull final TestableNetworkCallback systemDefaultCallback) {
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+ runWithShellPermissionIdentity(() ->
+ mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback,
+ new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
+ }
+
+ private static final class OnCompleteListenerCallback {
+ final CompletableFuture<Object> mDone = new CompletableFuture<>();
+
+ public void onComplete() {
+ mDone.complete(new Object());
+ }
+
+ void expectOnComplete() throws Exception {
+ try {
+ mDone.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ fail("Expected onComplete() not received after "
+ + NETWORK_CALLBACK_TIMEOUT_MS + " ms");
+ }
+ }
+ }
+
+ private void setOemNetworkPreferenceForMyPackage(final int networkPref) throws Exception {
+ final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+ .addNetworkPreference(mContext.getPackageName(), networkPref)
+ .build();
+ final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
+ mUiAutomation.adoptShellPermissionIdentity();
+ try {
+ mCm.setOemNetworkPreference(
+ pref, mContext.getMainExecutor(), oemPrefListener::onComplete);
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+ oemPrefListener.expectOnComplete();
+ }
+
+ /**
+ * This will clear the OEM network preference on the device. As there is currently no way of
+ * getting the existing preference, if this is executed while an existing preference is in
+ * place, that preference will need to be reapplied after executing this test.
+ * @throws Exception
+ */
+ private void clearOemNetworkPreference() throws Exception {
+ final OemNetworkPreferences clearPref = new OemNetworkPreferences.Builder().build();
+ final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
+ mUiAutomation.adoptShellPermissionIdentity();
+ try {
+ mCm.setOemNetworkPreference(
+ clearPref, mContext.getMainExecutor(), oemPrefListener::onComplete);
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+ oemPrefListener.expectOnComplete();
+ }
}