Allow to use ncm function for usb tethering am: 67bf9b5654
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/14782684 Change-Id: Ib8212ebcf5d6616b244e1dba65c9a16be2505302
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -64,6 +64,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
|
||||
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
|
||||
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
|
||||
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
|
||||
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.isCellular;
|
||||
|
||||
@@ -77,6 +78,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.EthernetManager;
|
||||
@@ -248,6 +250,7 @@ public class Tethering {
|
||||
private InterfaceSet mCurrentUpstreamIfaceSet;
|
||||
|
||||
private boolean mRndisEnabled; // track the RNDIS function enabled state
|
||||
private boolean mNcmEnabled; // track the NCM function enabled state
|
||||
// True iff. WiFi tethering should be started when soft AP is ready.
|
||||
private boolean mWifiTetherRequested;
|
||||
private Network mTetherUpstream;
|
||||
@@ -259,6 +262,7 @@ public class Tethering {
|
||||
private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
|
||||
private String mConfiguredEthernetIface;
|
||||
private EthernetCallback mEthernetCallback;
|
||||
private SettingsObserver mSettingsObserver;
|
||||
|
||||
public Tethering(TetheringDependencies deps) {
|
||||
mLog.mark("Tethering.constructed");
|
||||
@@ -310,6 +314,10 @@ public class Tethering {
|
||||
mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
|
||||
});
|
||||
|
||||
mSettingsObserver = new SettingsObserver(mHandler);
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(TETHER_FORCE_USB_FUNCTIONS), false, mSettingsObserver);
|
||||
|
||||
mStateReceiver = new StateReceiver();
|
||||
|
||||
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||
@@ -359,6 +367,28 @@ public class Tethering {
|
||||
startStateMachineUpdaters();
|
||||
}
|
||||
|
||||
private class SettingsObserver extends ContentObserver {
|
||||
SettingsObserver(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
mLog.i("OBSERVED Settings change");
|
||||
final boolean isUsingNcm = mConfig.isUsingNcm();
|
||||
updateConfiguration();
|
||||
if (isUsingNcm != mConfig.isUsingNcm()) {
|
||||
stopTetheringInternal(TETHERING_USB);
|
||||
stopTetheringInternal(TETHERING_NCM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ContentObserver getSettingsObserverForTest() {
|
||||
return mSettingsObserver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to register callbacks.
|
||||
* Call this function when tethering is ready to handle callback events.
|
||||
@@ -568,12 +598,15 @@ public class Tethering {
|
||||
|
||||
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
|
||||
@@ -851,6 +884,13 @@ public class Tethering {
|
||||
: IpServer.STATE_TETHERED;
|
||||
}
|
||||
|
||||
private int getRequestedUsbType(boolean forNcmFunction) {
|
||||
// TETHERING_NCM is only used if the device does not use NCM for regular USB tethering.
|
||||
if (forNcmFunction && !mConfig.isUsingNcm()) return TETHERING_NCM;
|
||||
|
||||
return TETHERING_USB;
|
||||
}
|
||||
|
||||
// TODO: Figure out how to update for local hotspot mode interfaces.
|
||||
private void sendTetherStateChangedBroadcast() {
|
||||
if (!isTetheringSupported()) return;
|
||||
@@ -987,8 +1027,8 @@ public class Tethering {
|
||||
final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
|
||||
final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false);
|
||||
|
||||
mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s",
|
||||
usbConnected, usbConfigured, rndisEnabled));
|
||||
mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
|
||||
usbConnected, usbConfigured, rndisEnabled, ncmEnabled));
|
||||
|
||||
// There are three types of ACTION_USB_STATE:
|
||||
//
|
||||
@@ -1005,19 +1045,22 @@ public class Tethering {
|
||||
// functions are ready to use.
|
||||
//
|
||||
// For more explanation, see b/62552150 .
|
||||
if (!usbConnected && mRndisEnabled) {
|
||||
if (!usbConnected && (mRndisEnabled || mNcmEnabled)) {
|
||||
// Turn off tethering if it was enabled and there is a disconnect.
|
||||
tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
|
||||
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);
|
||||
final int type = getRequestedUsbType(false /* isNcm */);
|
||||
final int state = getRequestedState(type);
|
||||
tetherMatchingInterfaces(state, type);
|
||||
} else if (usbConfigured && ncmEnabled) {
|
||||
final int type = getRequestedUsbType(true /* isNcm */);
|
||||
final int state = getRequestedState(type);
|
||||
tetherMatchingInterfaces(state, type);
|
||||
}
|
||||
mRndisEnabled = usbConfigured && rndisEnabled;
|
||||
mNcmEnabled = usbConfigured && ncmEnabled;
|
||||
}
|
||||
|
||||
private void handleWifiApAction(Intent intent) {
|
||||
@@ -1320,13 +1363,21 @@ public class Tethering {
|
||||
mLog.e("setUsbTethering: failed to get UsbManager!");
|
||||
return TETHER_ERROR_SERVICE_UNAVAIL;
|
||||
}
|
||||
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
|
||||
: UsbManager.FUNCTION_NONE);
|
||||
|
||||
final long usbFunction = mConfig.isUsingNcm()
|
||||
? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_RNDIS;
|
||||
usbManager.setCurrentFunctions(enable ? usbFunction : UsbManager.FUNCTION_NONE);
|
||||
|
||||
return TETHER_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
private int setNcmTethering(boolean enable) {
|
||||
if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")");
|
||||
|
||||
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
|
||||
// available.
|
||||
if (mConfig.isUsingNcm()) return TETHER_ERROR_SERVICE_UNAVAIL;
|
||||
|
||||
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
|
||||
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_NONE);
|
||||
return TETHER_ERROR_NO_ERROR;
|
||||
|
||||
@@ -23,11 +23,13 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
|
||||
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.net.TetheringConfigurationParcel;
|
||||
import android.net.util.SharedLog;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
@@ -75,6 +77,12 @@ public class TetheringConfiguration {
|
||||
|
||||
private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
|
||||
|
||||
@VisibleForTesting
|
||||
public static final int TETHER_USB_RNDIS_FUNCTION = 0;
|
||||
|
||||
@VisibleForTesting
|
||||
public static final int TETHER_USB_NCM_FUNCTION = 1;
|
||||
|
||||
/**
|
||||
* Override enabling BPF offload configuration for tethering.
|
||||
*/
|
||||
@@ -104,13 +112,20 @@ public class TetheringConfiguration {
|
||||
* via resource overlays, and later noticed issues. To that end, it overrides
|
||||
* config_tether_upstream_automatic when set to true.
|
||||
*
|
||||
* This flag is enabled if !=0 and less than the module APK version: see
|
||||
* This flag is enabled if !=0 and less than the module APEX version: see
|
||||
* {@link DeviceConfigUtils#isFeatureEnabled}. It is also ignored after R, as later devices
|
||||
* should just set config_tether_upstream_automatic to true instead.
|
||||
*/
|
||||
public static final String TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION =
|
||||
"tether_force_upstream_automatic_version";
|
||||
|
||||
/**
|
||||
* Settings key to foce choosing usb functions for usb tethering.
|
||||
*
|
||||
* TODO: Remove this hard code string and make Settings#TETHER_FORCE_USB_FUNCTIONS as API.
|
||||
*/
|
||||
public static final String TETHER_FORCE_USB_FUNCTIONS =
|
||||
"tether_force_usb_functions";
|
||||
/**
|
||||
* Default value that used to periodic polls tether offload stats from tethering offload HAL
|
||||
* to make the data warnings work.
|
||||
@@ -143,12 +158,17 @@ public class TetheringConfiguration {
|
||||
private final boolean mEnableWifiP2pDedicatedIp;
|
||||
|
||||
private final boolean mEnableSelectAllPrefixRange;
|
||||
private final int mUsbTetheringFunction;
|
||||
protected final ContentResolver mContentResolver;
|
||||
|
||||
public TetheringConfiguration(Context ctx, SharedLog log, int id) {
|
||||
final SharedLog configLog = log.forSubComponent("config");
|
||||
|
||||
activeDataSubId = id;
|
||||
Resources res = getResources(ctx, activeDataSubId);
|
||||
mContentResolver = ctx.getContentResolver();
|
||||
|
||||
mUsbTetheringFunction = getUsbTetheringFunction(res);
|
||||
|
||||
tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
|
||||
tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs);
|
||||
@@ -200,6 +220,11 @@ public class TetheringConfiguration {
|
||||
configLog.log(toString());
|
||||
}
|
||||
|
||||
/** Check whether using ncm for usb tethering */
|
||||
public boolean isUsingNcm() {
|
||||
return mUsbTetheringFunction == TETHER_USB_NCM_FUNCTION;
|
||||
}
|
||||
|
||||
/** Check whether input interface belong to usb.*/
|
||||
public boolean isUsb(String iface) {
|
||||
return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
|
||||
@@ -285,6 +310,9 @@ public class TetheringConfiguration {
|
||||
|
||||
pw.print("mEnableSelectAllPrefixRange: ");
|
||||
pw.println(mEnableSelectAllPrefixRange);
|
||||
|
||||
pw.print("mUsbTetheringFunction: ");
|
||||
pw.println(isUsingNcm() ? "NCM" : "RNDIS");
|
||||
}
|
||||
|
||||
/** Returns the string representation of this object.*/
|
||||
@@ -350,6 +378,26 @@ public class TetheringConfiguration {
|
||||
return mEnableSelectAllPrefixRange;
|
||||
}
|
||||
|
||||
private int getUsbTetheringFunction(Resources res) {
|
||||
final int valueFromRes = getResourceInteger(res, R.integer.config_tether_usb_functions,
|
||||
TETHER_USB_RNDIS_FUNCTION /* defaultValue */);
|
||||
return getSettingsIntValue(TETHER_FORCE_USB_FUNCTIONS, valueFromRes);
|
||||
}
|
||||
|
||||
private int getSettingsIntValue(final String name, final int defaultValue) {
|
||||
final String value = getSettingsValue(name);
|
||||
try {
|
||||
return value != null ? Integer.parseInt(value) : defaultValue;
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected String getSettingsValue(final String name) {
|
||||
return Settings.Global.getString(mContentResolver, name);
|
||||
}
|
||||
|
||||
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
|
||||
final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
|
||||
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
|
||||
|
||||
@@ -226,7 +226,7 @@ public final class EntitlementManagerTest {
|
||||
mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog,
|
||||
mPermissionChangeCallback);
|
||||
mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
|
||||
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
mEnMgr.setTetheringConfigurationFetcher(() -> {
|
||||
return mConfig;
|
||||
});
|
||||
@@ -251,7 +251,7 @@ public final class EntitlementManagerTest {
|
||||
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
|
||||
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
|
||||
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
|
||||
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -265,7 +265,7 @@ public final class EntitlementManagerTest {
|
||||
setupForRequiredProvisioning();
|
||||
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
|
||||
.thenReturn(null);
|
||||
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
// Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
|
||||
// Therefore provisioning still be required.
|
||||
assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
|
||||
@@ -275,7 +275,7 @@ public final class EntitlementManagerTest {
|
||||
public void toleratesCarrierConfigMissing() {
|
||||
setupForRequiredProvisioning();
|
||||
when(mCarrierConfigManager.getConfig()).thenReturn(null);
|
||||
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
// We still have a provisioning app configured, so still require provisioning.
|
||||
assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
|
||||
}
|
||||
@@ -293,11 +293,11 @@ public final class EntitlementManagerTest {
|
||||
setupForRequiredProvisioning();
|
||||
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
|
||||
.thenReturn(null);
|
||||
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
|
||||
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
|
||||
.thenReturn(new String[] {"malformedApp"});
|
||||
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,9 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -35,6 +38,7 @@ import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ModuleInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -42,12 +46,15 @@ import android.content.res.Resources;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Build;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.test.BroadcastInterceptingContext;
|
||||
import com.android.internal.util.test.FakeSettingsProvider;
|
||||
import com.android.net.module.util.DeviceConfigUtils;
|
||||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||
@@ -78,6 +85,7 @@ public class TetheringConfigurationTest {
|
||||
private static final String TEST_PACKAGE_NAME = "com.android.tethering.test";
|
||||
private static final String APEX_NAME = "com.android.tethering";
|
||||
private static final long TEST_PACKAGE_VERSION = 1234L;
|
||||
@Mock private ApplicationInfo mApplicationInfo;
|
||||
@Mock private Context mContext;
|
||||
@Mock private TelephonyManager mTelephonyManager;
|
||||
@Mock private Resources mResources;
|
||||
@@ -88,6 +96,7 @@ public class TetheringConfigurationTest {
|
||||
private boolean mHasTelephonyManager;
|
||||
private boolean mEnableLegacyDhcpServer;
|
||||
private MockitoSession mMockingSession;
|
||||
private MockContentResolver mContentResolver;
|
||||
|
||||
private class MockTetheringConfiguration extends TetheringConfiguration {
|
||||
MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
|
||||
@@ -105,6 +114,11 @@ public class TetheringConfigurationTest {
|
||||
super(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationInfo getApplicationInfo() {
|
||||
return mApplicationInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
return mResources;
|
||||
@@ -153,7 +167,8 @@ public class TetheringConfigurationTest {
|
||||
new String[0]);
|
||||
when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
|
||||
TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
|
||||
when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
|
||||
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
|
||||
.thenReturn(new String[]{ "test_usb\\d" });
|
||||
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
|
||||
.thenReturn(new String[]{ "test_wlan\\d" });
|
||||
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn(
|
||||
@@ -171,12 +186,20 @@ public class TetheringConfigurationTest {
|
||||
mHasTelephonyManager = true;
|
||||
mMockContext = new MockContext(mContext);
|
||||
mEnableLegacyDhcpServer = false;
|
||||
|
||||
mContentResolver = new MockContentResolver(mMockContext);
|
||||
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
||||
when(mContext.getContentResolver()).thenReturn(mContentResolver);
|
||||
// Call {@link #clearSettingsProvider()} before and after using FakeSettingsProvider.
|
||||
FakeSettingsProvider.clearSettingsProvider();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mMockingSession.finishMocking();
|
||||
DeviceConfigUtils.resetPackageVersionCacheForTest();
|
||||
// Call {@link #clearSettingsProvider()} before and after using FakeSettingsProvider.
|
||||
FakeSettingsProvider.clearSettingsProvider();
|
||||
}
|
||||
|
||||
private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
|
||||
@@ -539,4 +562,42 @@ public class TetheringConfigurationTest {
|
||||
assertEquals(value, new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)
|
||||
.chooseUpstreamAutomatically);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsbTetheringFunctions() throws Exception {
|
||||
// Test default value. If both resource and settings is not configured, usingNcm is false.
|
||||
assertIsUsingNcm(false /* usingNcm */);
|
||||
|
||||
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
|
||||
TETHER_USB_NCM_FUNCTION);
|
||||
assertIsUsingNcm(true /* usingNcm */);
|
||||
|
||||
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
|
||||
TETHER_USB_RNDIS_FUNCTION);
|
||||
assertIsUsingNcm(false /* usingNcm */);
|
||||
|
||||
setTetherForceUsbFunctions(TETHER_USB_RNDIS_FUNCTION);
|
||||
assertIsUsingNcm(false /* usingNcm */);
|
||||
|
||||
setTetherForceUsbFunctions(TETHER_USB_NCM_FUNCTION);
|
||||
assertIsUsingNcm(true /* usingNcm */);
|
||||
|
||||
// Test throws NumberFormatException.
|
||||
setTetherForceUsbFunctions("WrongNumberFormat");
|
||||
assertIsUsingNcm(false /* usingNcm */);
|
||||
}
|
||||
|
||||
private void assertIsUsingNcm(boolean expected) {
|
||||
final TetheringConfiguration cfg =
|
||||
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||
assertEquals(expected, cfg.isUsingNcm());
|
||||
}
|
||||
|
||||
private void setTetherForceUsbFunctions(final String value) {
|
||||
Settings.Global.putString(mContentResolver, TETHER_FORCE_USB_FUNCTIONS, value);
|
||||
}
|
||||
|
||||
private void setTetherForceUsbFunctions(final int value) {
|
||||
setTetherForceUsbFunctions(Integer.toString(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import static android.net.TetheringManager.TETHERING_WIFI;
|
||||
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
|
||||
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
|
||||
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
|
||||
@@ -68,6 +69,9 @@ import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOA
|
||||
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.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.net.ConnectivityManager.NetworkCallback;
|
||||
import android.net.EthernetManager;
|
||||
@@ -181,7 +186,6 @@ import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAge
|
||||
import com.android.testutils.MiscAsserts;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@@ -210,7 +214,7 @@ public class TetheringTest {
|
||||
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
|
||||
private static final String TEST_DUN_IFNAME = "test_dun0";
|
||||
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
|
||||
private static final String TEST_USB_IFNAME = "test_rndis0";
|
||||
private static final String TEST_RNDIS_IFNAME = "test_rndis0";
|
||||
private static final String TEST_WIFI_IFNAME = "test_wlan0";
|
||||
private static final String TEST_WLAN_IFNAME = "test_wlan1";
|
||||
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
|
||||
@@ -220,6 +224,11 @@ public class TetheringTest {
|
||||
private static final String TETHERING_NAME = "Tethering";
|
||||
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
|
||||
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
|
||||
private static final String TEST_RNDIS_REGEX = "test_rndis\\d";
|
||||
private static final String TEST_NCM_REGEX = "test_ncm\\d";
|
||||
private static final String TEST_WIFI_REGEX = "test_wlan\\d";
|
||||
private static final String TEST_P2P_REGEX = "test_p2p-p2p\\d-.*";
|
||||
private static final String TEST_BT_REGEX = "test_pan\\d";
|
||||
|
||||
private static final int CELLULAR_NETID = 100;
|
||||
private static final int WIFI_NETID = 101;
|
||||
@@ -342,7 +351,7 @@ public class TetheringTest {
|
||||
@Override
|
||||
public InterfaceParams getInterfaceParams(String ifName) {
|
||||
assertTrue("Non-mocked interface " + ifName,
|
||||
ifName.equals(TEST_USB_IFNAME)
|
||||
ifName.equals(TEST_RNDIS_IFNAME)
|
||||
|| ifName.equals(TEST_WLAN_IFNAME)
|
||||
|| ifName.equals(TEST_WIFI_IFNAME)
|
||||
|| ifName.equals(TEST_MOBILE_IFNAME)
|
||||
@@ -352,7 +361,7 @@ public class TetheringTest {
|
||||
|| ifName.equals(TEST_ETH_IFNAME)
|
||||
|| ifName.equals(TEST_BT_IFNAME));
|
||||
final String[] ifaces = new String[] {
|
||||
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_WIFI_IFNAME, TEST_MOBILE_IFNAME,
|
||||
TEST_RNDIS_IFNAME, TEST_WLAN_IFNAME, TEST_WIFI_IFNAME, TEST_MOBILE_IFNAME,
|
||||
TEST_DUN_IFNAME, TEST_P2P_IFNAME, TEST_NCM_IFNAME, TEST_ETH_IFNAME};
|
||||
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
|
||||
MacAddress.ALL_ZEROS_ADDRESS);
|
||||
@@ -376,28 +385,6 @@ public class TetheringTest {
|
||||
}
|
||||
}
|
||||
|
||||
// MyTetheringConfiguration is used to override static method for testing.
|
||||
private class MyTetheringConfiguration extends TetheringConfiguration {
|
||||
MyTetheringConfiguration(Context ctx, SharedLog log, int id) {
|
||||
super(ctx, log, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDeviceConfigProperty(final String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFeatureEnabled(Context ctx, String featureVersionFlag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
|
||||
return mResources;
|
||||
}
|
||||
}
|
||||
|
||||
public class MockTetheringDependencies extends TetheringDependencies {
|
||||
StateMachine mUpstreamNetworkMonitorSM;
|
||||
ArrayList<IpServer> mIpv6CoordinatorNotifyList;
|
||||
@@ -464,7 +451,7 @@ public class TetheringTest {
|
||||
@Override
|
||||
public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
|
||||
int subId) {
|
||||
mConfig = spy(new MyTetheringConfiguration(ctx, log, subId));
|
||||
mConfig = spy(new FakeTetheringConfiguration(ctx, log, subId));
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
@@ -593,18 +580,13 @@ public class TetheringTest {
|
||||
new Network(DUN_NETID));
|
||||
}
|
||||
|
||||
// See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and
|
||||
// after use.
|
||||
// See FakeSettingsProvider#clearSettingsProvider() that this also needs to be called before
|
||||
// use.
|
||||
@BeforeClass
|
||||
public static void setupOnce() {
|
||||
FakeSettingsProvider.clearSettingsProvider();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownOnce() {
|
||||
FakeSettingsProvider.clearSettingsProvider();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -614,7 +596,7 @@ public class TetheringTest {
|
||||
false);
|
||||
when(mNetd.interfaceGetList())
|
||||
.thenReturn(new String[] {
|
||||
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
|
||||
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_RNDIS_IFNAME, TEST_P2P_IFNAME,
|
||||
TEST_NCM_IFNAME, TEST_ETH_IFNAME, TEST_BT_IFNAME});
|
||||
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
|
||||
mInterfaceConfiguration = new InterfaceConfigurationParcel();
|
||||
@@ -667,17 +649,19 @@ public class TetheringTest {
|
||||
supported ? 1 : 0);
|
||||
when(mUserManager.hasUserRestriction(
|
||||
UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported);
|
||||
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
|
||||
TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION);
|
||||
// Setup tetherable configuration.
|
||||
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
|
||||
.thenReturn(new String[] { "test_rndis\\d" });
|
||||
.thenReturn(new String[] { TEST_RNDIS_REGEX});
|
||||
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
|
||||
.thenReturn(new String[] { "test_wlan\\d" });
|
||||
.thenReturn(new String[] { TEST_WIFI_REGEX });
|
||||
when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
|
||||
.thenReturn(new String[] { "test_p2p-p2p\\d-.*" });
|
||||
.thenReturn(new String[] { TEST_P2P_REGEX });
|
||||
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
|
||||
.thenReturn(new String[] { "test_pan\\d" });
|
||||
.thenReturn(new String[] { TEST_BT_REGEX });
|
||||
when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
|
||||
.thenReturn(new String[] { "test_ncm\\d" });
|
||||
.thenReturn(new String[] { TEST_NCM_REGEX });
|
||||
when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
|
||||
new int[] { TYPE_WIFI, TYPE_MOBILE_DUN });
|
||||
when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
|
||||
@@ -714,6 +698,7 @@ public class TetheringTest {
|
||||
@After
|
||||
public void tearDown() {
|
||||
mServiceContext.unregisterReceiver(mBroadcastReceiver);
|
||||
FakeSettingsProvider.clearSettingsProvider();
|
||||
}
|
||||
|
||||
private void sendWifiApStateChanged(int state) {
|
||||
@@ -759,16 +744,18 @@ public class TetheringTest {
|
||||
mLooper.dispatchAll();
|
||||
}
|
||||
|
||||
private void sendUsbBroadcast(boolean connected, boolean configured, boolean function,
|
||||
int type) {
|
||||
private boolean tetherUsbFunctionMatches(int function, int enabledType) {
|
||||
return function == enabledType;
|
||||
}
|
||||
|
||||
private void sendUsbBroadcast(boolean connected, boolean configured, int function) {
|
||||
final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
|
||||
intent.putExtra(USB_CONNECTED, connected);
|
||||
intent.putExtra(USB_CONFIGURED, configured);
|
||||
if (type == TETHERING_USB) {
|
||||
intent.putExtra(USB_FUNCTION_RNDIS, function);
|
||||
} else {
|
||||
intent.putExtra(USB_FUNCTION_NCM, function);
|
||||
}
|
||||
intent.putExtra(USB_FUNCTION_RNDIS,
|
||||
tetherUsbFunctionMatches(TETHER_USB_RNDIS_FUNCTION, function));
|
||||
intent.putExtra(USB_FUNCTION_NCM,
|
||||
tetherUsbFunctionMatches(TETHER_USB_NCM_FUNCTION, function));
|
||||
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
|
||||
mLooper.dispatchAll();
|
||||
}
|
||||
@@ -850,11 +837,18 @@ public class TetheringTest {
|
||||
final TetheringRequestParcel request = createTetheringRequestParcel(TETHERING_USB);
|
||||
mTethering.startTethering(request, null);
|
||||
mLooper.dispatchAll();
|
||||
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
|
||||
|
||||
assertEquals(1, mTethering.getActiveTetheringRequests().size());
|
||||
assertEquals(request, mTethering.getActiveTetheringRequests().get(TETHERING_USB));
|
||||
|
||||
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
|
||||
if (mTethering.getTetheringConfiguration().isUsingNcm()) {
|
||||
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NCM);
|
||||
mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
|
||||
} else {
|
||||
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
|
||||
mTethering.interfaceStatusChanged(TEST_RNDIS_IFNAME, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -867,7 +861,7 @@ public class TetheringTest {
|
||||
verifyNoMoreInteractions(mNetd);
|
||||
|
||||
// Pretend we then receive USB configured broadcast.
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
|
||||
// Now we should see the start of tethering mechanics (in this case:
|
||||
// tetherMatchingInterfaces() which starts by fetching all interfaces).
|
||||
verify(mNetd, times(1)).interfaceGetList();
|
||||
@@ -939,16 +933,13 @@ public class TetheringTest {
|
||||
*/
|
||||
private void sendIPv6TetherUpdates(UpstreamNetworkState upstreamState) {
|
||||
// IPv6TetheringCoordinator must have been notified of downstream
|
||||
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
|
||||
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
|
||||
eq(IpServer.STATE_TETHERED));
|
||||
|
||||
for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) {
|
||||
UpstreamNetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
|
||||
ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
|
||||
upstreamState.linkProperties.isIpv6Provisioned()
|
||||
? ipv6OnlyState.linkProperties
|
||||
: null);
|
||||
break;
|
||||
}
|
||||
mLooper.dispatchAll();
|
||||
}
|
||||
@@ -956,7 +947,18 @@ public class TetheringTest {
|
||||
private void runUsbTethering(UpstreamNetworkState upstreamState) {
|
||||
initTetheringUpstream(upstreamState);
|
||||
prepareUsbTethering();
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
if (mTethering.getTetheringConfiguration().isUsingNcm()) {
|
||||
sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
|
||||
verify(mIPv6TetheringCoordinator).addActiveDownstream(
|
||||
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_NCM_IFNAME)),
|
||||
eq(IpServer.STATE_TETHERED));
|
||||
} else {
|
||||
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
|
||||
verify(mIPv6TetheringCoordinator).addActiveDownstream(
|
||||
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_RNDIS_IFNAME)),
|
||||
eq(IpServer.STATE_TETHERED));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void assertSetIfaceToDadProxy(final int numOfCalls, final String ifaceName) {
|
||||
@@ -972,8 +974,8 @@ public class TetheringTest {
|
||||
UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
assertSetIfaceToDadProxy(0 /* numOfCalls */, "" /* ifaceName */);
|
||||
@@ -999,8 +1001,8 @@ public class TetheringTest {
|
||||
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
// TODO: add interfaceParams to compare in verify.
|
||||
@@ -1014,8 +1016,8 @@ public class TetheringTest {
|
||||
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mRouterAdvertisementDaemon, times(1)).start();
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||
any(), any());
|
||||
@@ -1031,12 +1033,13 @@ public class TetheringTest {
|
||||
UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||
any(), any());
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME,
|
||||
TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
|
||||
@@ -1046,14 +1049,20 @@ public class TetheringTest {
|
||||
|
||||
@Test
|
||||
public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
|
||||
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
|
||||
TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
|
||||
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
|
||||
.thenReturn(new String[] {TEST_NCM_REGEX});
|
||||
sendConfigurationChanged();
|
||||
|
||||
// Setup IPv6
|
||||
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||
any(), any());
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
// Then 464xlat comes up
|
||||
upstreamState = buildMobile464xlatUpstreamState();
|
||||
@@ -1068,11 +1077,12 @@ public class TetheringTest {
|
||||
mLooper.dispatchAll();
|
||||
|
||||
// Forwarding is added for 464xlat
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME,
|
||||
TEST_XLAT_MOBILE_IFNAME);
|
||||
// Forwarding was not re-added for v6 (still times(1))
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
|
||||
// DHCP not restarted on downstream (still times(1))
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||
any(), any());
|
||||
@@ -1104,7 +1114,7 @@ public class TetheringTest {
|
||||
|
||||
// Start USB tethering with no current upstream.
|
||||
prepareUsbTethering();
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
|
||||
inOrder.verify(mUpstreamNetworkMonitor).startObserveAllNetworks();
|
||||
inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
|
||||
|
||||
@@ -1300,7 +1310,7 @@ public class TetheringTest {
|
||||
|
||||
// Start USB tethering with no current upstream.
|
||||
prepareUsbTethering();
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
|
||||
inOrder.verify(mUpstreamNetworkMonitor).startObserveAllNetworks();
|
||||
inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
|
||||
ArgumentCaptor<NetworkCallback> captor = ArgumentCaptor.forClass(NetworkCallback.class);
|
||||
@@ -1334,7 +1344,7 @@ public class TetheringTest {
|
||||
|
||||
private void runNcmTethering() {
|
||||
prepareNcmTethering();
|
||||
sendUsbBroadcast(true, true, true, TETHERING_NCM);
|
||||
sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1614,7 +1624,7 @@ public class TetheringTest {
|
||||
// Start usb tethering and check that usb interface is tethered.
|
||||
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
|
||||
assertTrue(mTethering.isTetheringActive());
|
||||
assertEquals(0, mTethering.getActiveTetheringRequests().size());
|
||||
|
||||
@@ -1854,7 +1864,7 @@ public class TetheringTest {
|
||||
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
|
||||
runStopUSBTethering();
|
||||
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
|
||||
reset(mUsbManager);
|
||||
reset(mUsbManager, mIPv6TetheringCoordinator);
|
||||
// 2. Offload fail if no OffloadControl.
|
||||
initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_NONE,
|
||||
0 /* defaultDisabled */);
|
||||
@@ -1862,7 +1872,7 @@ public class TetheringTest {
|
||||
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
|
||||
runStopUSBTethering();
|
||||
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
|
||||
reset(mUsbManager);
|
||||
reset(mUsbManager, mIPv6TetheringCoordinator);
|
||||
// 3. Offload fail if disabled by settings.
|
||||
initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0,
|
||||
1 /* defaultDisabled */);
|
||||
@@ -1875,8 +1885,10 @@ public class TetheringTest {
|
||||
private void runStopUSBTethering() {
|
||||
mTethering.stopTethering(TETHERING_USB);
|
||||
mLooper.dispatchAll();
|
||||
mTethering.interfaceRemoved(TEST_USB_IFNAME);
|
||||
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
|
||||
sendUsbBroadcast(true, true, -1 /* function */);
|
||||
mLooper.dispatchAll();
|
||||
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||
}
|
||||
|
||||
private void initOffloadConfiguration(final boolean offloadConfig,
|
||||
@@ -2052,29 +2064,29 @@ public class TetheringTest {
|
||||
// Start Tethering.
|
||||
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
|
||||
// Data saver is ON.
|
||||
setDataSaverEnabled(true);
|
||||
// Verify that tethering should be disabled.
|
||||
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||
mTethering.interfaceRemoved(TEST_USB_IFNAME);
|
||||
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
|
||||
mLooper.dispatchAll();
|
||||
assertEquals(mTethering.getTetheredIfaces(), new String[0]);
|
||||
reset(mUsbManager);
|
||||
reset(mUsbManager, mIPv6TetheringCoordinator);
|
||||
|
||||
runUsbTethering(upstreamState);
|
||||
// Verify that user can start tethering again without turning OFF data saver.
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
|
||||
|
||||
// If data saver is keep ON with change event, tethering should not be OFF this time.
|
||||
setDataSaverEnabled(true);
|
||||
verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
|
||||
|
||||
// If data saver is turned OFF, it should not change tethering.
|
||||
setDataSaverEnabled(false);
|
||||
verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
|
||||
assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_RNDIS_IFNAME);
|
||||
}
|
||||
|
||||
private static <T> void assertContains(Collection<T> collection, T element) {
|
||||
@@ -2134,8 +2146,8 @@ public class TetheringTest {
|
||||
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
|
||||
|
||||
// Expect that when USB comes up, the DHCP server is configured with the requested address.
|
||||
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
mTethering.interfaceStatusChanged(TEST_RNDIS_IFNAME, true);
|
||||
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
|
||||
any(), any());
|
||||
verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
|
||||
@@ -2143,6 +2155,12 @@ public class TetheringTest {
|
||||
|
||||
@Test
|
||||
public void testRequestStaticIp() throws Exception {
|
||||
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
|
||||
TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
|
||||
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
|
||||
.thenReturn(new String[] {TEST_NCM_REGEX});
|
||||
sendConfigurationChanged();
|
||||
|
||||
final LinkAddress serverLinkAddr = new LinkAddress("192.168.0.123/24");
|
||||
final LinkAddress clientLinkAddr = new LinkAddress("192.168.0.42/24");
|
||||
final String serverAddr = "192.168.0.123";
|
||||
@@ -2152,9 +2170,9 @@ public class TetheringTest {
|
||||
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
|
||||
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), null);
|
||||
mLooper.dispatchAll();
|
||||
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
|
||||
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
|
||||
mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
|
||||
sendUsbBroadcast(true, true, TETHER_USB_NCM_FUNCTION);
|
||||
verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
|
||||
verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(),
|
||||
any());
|
||||
@@ -2321,7 +2339,7 @@ public class TetheringTest {
|
||||
wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI);
|
||||
// verify turn off usb tethering
|
||||
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||
mTethering.interfaceRemoved(TEST_USB_IFNAME);
|
||||
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
|
||||
mLooper.dispatchAll();
|
||||
// verify restart usb tethering
|
||||
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
|
||||
@@ -2362,7 +2380,7 @@ public class TetheringTest {
|
||||
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||
// verify turn off ethernet tethering
|
||||
verify(mockRequest).release();
|
||||
mTethering.interfaceRemoved(TEST_USB_IFNAME);
|
||||
mTethering.interfaceRemoved(TEST_RNDIS_IFNAME);
|
||||
ethCallback.onUnavailable();
|
||||
mLooper.dispatchAll();
|
||||
// verify restart usb tethering
|
||||
@@ -2375,14 +2393,15 @@ public class TetheringTest {
|
||||
reset(mUsbManager, mEm);
|
||||
when(mNetd.interfaceGetList())
|
||||
.thenReturn(new String[] {
|
||||
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
|
||||
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_RNDIS_IFNAME, TEST_P2P_IFNAME,
|
||||
TEST_NCM_IFNAME, TEST_ETH_IFNAME});
|
||||
|
||||
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_USB_IFNAME);
|
||||
mTethering.interfaceStatusChanged(TEST_RNDIS_IFNAME, true);
|
||||
sendUsbBroadcast(true, true, TETHER_USB_RNDIS_FUNCTION);
|
||||
assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_RNDIS_IFNAME);
|
||||
assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_ETH_IFNAME);
|
||||
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(TEST_USB_IFNAME));
|
||||
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(
|
||||
TEST_RNDIS_IFNAME));
|
||||
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(TEST_ETH_IFNAME));
|
||||
}
|
||||
|
||||
@@ -2573,6 +2592,46 @@ public class TetheringTest {
|
||||
reset(mBluetoothAdapter, mBluetoothPan);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsbTetheringWithNcmFunction() throws Exception {
|
||||
when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
|
||||
TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
|
||||
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
|
||||
.thenReturn(new String[] {TEST_NCM_REGEX});
|
||||
sendConfigurationChanged();
|
||||
|
||||
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
|
||||
// available.
|
||||
final ResultListener ncmResult = new ResultListener(TETHER_ERROR_SERVICE_UNAVAIL);
|
||||
mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), ncmResult);
|
||||
mLooper.dispatchAll();
|
||||
ncmResult.assertHasResult();
|
||||
|
||||
final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
|
||||
runUsbTethering(upstreamState);
|
||||
|
||||
verify(mNetd).interfaceGetList();
|
||||
verify(mNetd).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
|
||||
verify(mNetd).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
|
||||
|
||||
verify(mRouterAdvertisementDaemon).start();
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
|
||||
any(), any());
|
||||
sendIPv6TetherUpdates(upstreamState);
|
||||
assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
|
||||
verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
|
||||
verify(mNetd).tetherApplyDnsInterfaces();
|
||||
|
||||
Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS,
|
||||
TETHER_USB_RNDIS_FUNCTION);
|
||||
final ContentObserver observer = mTethering.getSettingsObserverForTest();
|
||||
observer.onChange(false /* selfChange */);
|
||||
mLooper.dispatchAll();
|
||||
// stop TETHERING_USB and TETHERING_NCM
|
||||
verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
|
||||
mTethering.interfaceRemoved(TEST_NCM_IFNAME);
|
||||
sendUsbBroadcast(true, true, -1 /* function */);
|
||||
}
|
||||
// TODO: Test that a request for hotspot mode doesn't interfere with an
|
||||
// already operating tethering mode interface.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user