Merge changes I34842acd,Icc6c4d6b
* changes: Add support for Ethernet tethering Local Tethering with ncm interface
This commit is contained in:
@@ -130,6 +130,18 @@ public class TetheringManager {
|
||||
*/
|
||||
public static final int TETHERING_WIFI_P2P = 3;
|
||||
|
||||
/**
|
||||
* Ncm local tethering type.
|
||||
* @see #startTethering(TetheringRequest, Executor, StartTetheringCallback)
|
||||
*/
|
||||
public static final int TETHERING_NCM = 4;
|
||||
|
||||
/**
|
||||
* Ethernet tethering type.
|
||||
* @see #startTethering(TetheringRequest, Executor, StartTetheringCallback)
|
||||
*/
|
||||
public static final int TETHERING_ETHERNET = 5;
|
||||
|
||||
public static final int TETHER_ERROR_NO_ERROR = 0;
|
||||
public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
|
||||
public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
<item>"rndis\\d"</item>
|
||||
</string-array>
|
||||
|
||||
<!-- List of regexpressions describing the interface (if any) that represent tetherable
|
||||
NCM interfaces. If the device doesn't want to support tethering over NCM this should
|
||||
be empty. -->
|
||||
<string-array translatable="false" name="config_tether_ncm_regexs">
|
||||
</string-array>
|
||||
|
||||
<!-- List of regexpressions describing the interface (if any) that represent tetherable
|
||||
Wifi interfaces. If the device doesn't want to support tethering over Wifi this
|
||||
should be empty. An example would be "softap.*" -->
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<overlayable name="TetheringConfig">
|
||||
<policy type="product|system|vendor">
|
||||
<item type="array" name="config_tether_usb_regexs"/>
|
||||
<item type="array" name="config_tether_ncm_regexs" />
|
||||
<item type="array" name="config_tether_wifi_regexs"/>
|
||||
<item type="array" name="config_tether_wifi_p2p_regexs"/>
|
||||
<item type="array" name="config_tether_bluetooth_regexs"/>
|
||||
|
||||
@@ -93,6 +93,8 @@ public class IpServer extends StateMachine {
|
||||
private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
|
||||
private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1";
|
||||
private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24;
|
||||
private static final String ETHERNET_IFACE_ADDR = "192.168.50.1";
|
||||
private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24;
|
||||
|
||||
// TODO: have PanService use some visible version of this constant
|
||||
private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
|
||||
@@ -416,7 +418,8 @@ public class IpServer extends StateMachine {
|
||||
final Inet4Address srvAddr;
|
||||
int prefixLen = 0;
|
||||
try {
|
||||
if (mInterfaceType == TetheringManager.TETHERING_USB) {
|
||||
if (mInterfaceType == TetheringManager.TETHERING_USB
|
||||
|| mInterfaceType == TetheringManager.TETHERING_NCM) {
|
||||
srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
|
||||
prefixLen = USB_PREFIX_LENGTH;
|
||||
} else if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
|
||||
@@ -425,6 +428,10 @@ public class IpServer extends StateMachine {
|
||||
} else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
|
||||
srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR);
|
||||
prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
|
||||
} else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) {
|
||||
// TODO: randomize address for tethering too, similarly to wifi
|
||||
srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR);
|
||||
prefixLen = ETHERNET_IFACE_PREFIX_LENGTH;
|
||||
} else {
|
||||
// BT configures the interface elsewhere: only start DHCP.
|
||||
// TODO: make all tethering types behave the same way, and delete the bluetooth
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.connectivity.tethering;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
|
||||
import static android.hardware.usb.UsbManager.USB_CONNECTED;
|
||||
import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM;
|
||||
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
|
||||
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
|
||||
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
|
||||
@@ -29,7 +30,9 @@ import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
|
||||
import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
|
||||
import static android.net.TetheringManager.EXTRA_ERRORED_TETHER;
|
||||
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
||||
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
||||
import static android.net.TetheringManager.TETHERING_INVALID;
|
||||
import static android.net.TetheringManager.TETHERING_NCM;
|
||||
import static android.net.TetheringManager.TETHERING_USB;
|
||||
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||
@@ -66,6 +69,7 @@ import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.EthernetManager;
|
||||
import android.net.IIntResultListener;
|
||||
import android.net.INetd;
|
||||
import android.net.ITetheringEventCallback;
|
||||
@@ -110,6 +114,7 @@ import android.util.SparseArray;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.MessageUtils;
|
||||
@@ -212,6 +217,13 @@ public class Tethering {
|
||||
private boolean mDataSaverEnabled = false;
|
||||
private String mWifiP2pTetherInterface = null;
|
||||
|
||||
@GuardedBy("mPublicSync")
|
||||
private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
|
||||
@GuardedBy("mPublicSync")
|
||||
private String mConfiguredEthernetIface;
|
||||
@GuardedBy("mPublicSync")
|
||||
private EthernetCallback mEthernetCallback;
|
||||
|
||||
public Tethering(TetheringDependencies deps) {
|
||||
mLog.mark("Tethering.constructed");
|
||||
mDeps = deps;
|
||||
@@ -408,6 +420,8 @@ public class Tethering {
|
||||
return TETHERING_USB;
|
||||
} else if (cfg.isBluetooth(iface)) {
|
||||
return TETHERING_BLUETOOTH;
|
||||
} else if (cfg.isNcm(iface)) {
|
||||
return TETHERING_NCM;
|
||||
}
|
||||
return TETHERING_INVALID;
|
||||
}
|
||||
@@ -456,6 +470,14 @@ public class Tethering {
|
||||
case TETHERING_BLUETOOTH:
|
||||
setBluetoothTethering(enable, listener);
|
||||
break;
|
||||
case TETHERING_NCM:
|
||||
result = setNcmTethering(enable);
|
||||
sendTetherResult(listener, result);
|
||||
break;
|
||||
case TETHERING_ETHERNET:
|
||||
result = setEthernetTethering(enable);
|
||||
sendTetherResult(listener, result);
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "Invalid tether type.");
|
||||
sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE);
|
||||
@@ -532,6 +554,57 @@ public class Tethering {
|
||||
}, BluetoothProfile.PAN);
|
||||
}
|
||||
|
||||
private int setEthernetTethering(final boolean enable) {
|
||||
final EthernetManager em = (EthernetManager) mContext.getSystemService(
|
||||
Context.ETHERNET_SERVICE);
|
||||
synchronized (mPublicSync) {
|
||||
if (enable) {
|
||||
mEthernetCallback = new EthernetCallback();
|
||||
mEthernetIfaceRequest = em.requestTetheredInterface(mEthernetCallback);
|
||||
} else {
|
||||
if (mConfiguredEthernetIface != null) {
|
||||
stopEthernetTetheringLocked();
|
||||
mEthernetIfaceRequest.release();
|
||||
}
|
||||
mEthernetCallback = null;
|
||||
}
|
||||
}
|
||||
return TETHER_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
private void stopEthernetTetheringLocked() {
|
||||
if (mConfiguredEthernetIface == null) return;
|
||||
changeInterfaceState(mConfiguredEthernetIface, IpServer.STATE_AVAILABLE);
|
||||
stopTrackingInterfaceLocked(mConfiguredEthernetIface);
|
||||
mConfiguredEthernetIface = null;
|
||||
}
|
||||
|
||||
private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback {
|
||||
@Override
|
||||
public void onAvailable(String iface) {
|
||||
synchronized (mPublicSync) {
|
||||
if (this != mEthernetCallback) {
|
||||
// Ethernet callback arrived after Ethernet tethering stopped. Ignore.
|
||||
return;
|
||||
}
|
||||
maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET);
|
||||
changeInterfaceState(iface, IpServer.STATE_TETHERED);
|
||||
mConfiguredEthernetIface = iface;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnavailable() {
|
||||
synchronized (mPublicSync) {
|
||||
if (this != mEthernetCallback) {
|
||||
// onAvailable called after stopping Ethernet tethering.
|
||||
return;
|
||||
}
|
||||
stopEthernetTetheringLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int tether(String iface) {
|
||||
return tether(iface, IpServer.STATE_TETHERED);
|
||||
}
|
||||
@@ -582,6 +655,7 @@ public class Tethering {
|
||||
stopTethering(TETHERING_WIFI_P2P);
|
||||
stopTethering(TETHERING_USB);
|
||||
stopTethering(TETHERING_BLUETOOTH);
|
||||
stopTethering(TETHERING_ETHERNET);
|
||||
}
|
||||
|
||||
int getLastTetherError(String iface) {
|
||||
@@ -805,6 +879,7 @@ public class Tethering {
|
||||
final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false);
|
||||
final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false);
|
||||
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));
|
||||
@@ -832,6 +907,8 @@ public class Tethering {
|
||||
} else if (usbConfigured && rndisEnabled) {
|
||||
// Tether if rndis is enabled and usb is configured.
|
||||
tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
|
||||
} else if (usbConnected && ncmEnabled) {
|
||||
tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM);
|
||||
}
|
||||
mRndisEnabled = usbConfigured && rndisEnabled;
|
||||
}
|
||||
@@ -1133,6 +1210,16 @@ public class Tethering {
|
||||
return TETHER_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
private int setNcmTethering(boolean enable) {
|
||||
if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")");
|
||||
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
|
||||
synchronized (mPublicSync) {
|
||||
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM
|
||||
: UsbManager.FUNCTION_NONE);
|
||||
}
|
||||
return TETHER_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
// TODO review API - figure out how to delete these entirely.
|
||||
String[] getTetheredIfaces() {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
|
||||
@@ -83,6 +83,7 @@ public class TetheringConfiguration {
|
||||
public final String[] tetherableWifiRegexs;
|
||||
public final String[] tetherableWifiP2pRegexs;
|
||||
public final String[] tetherableBluetoothRegexs;
|
||||
public final String[] tetherableNcmRegexs;
|
||||
public final boolean isDunRequired;
|
||||
public final boolean chooseUpstreamAutomatically;
|
||||
public final Collection<Integer> preferredUpstreamIfaceTypes;
|
||||
@@ -103,6 +104,7 @@ public class TetheringConfiguration {
|
||||
Resources res = getResources(ctx, activeDataSubId);
|
||||
|
||||
tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
|
||||
tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs);
|
||||
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
|
||||
// us an interface name. Careful consideration needs to be given to
|
||||
// implications for Settings and for provisioning checks.
|
||||
@@ -156,6 +158,11 @@ public class TetheringConfiguration {
|
||||
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
|
||||
}
|
||||
|
||||
/** Check if interface is ncm */
|
||||
public boolean isNcm(String iface) {
|
||||
return matchesDownstreamRegexs(iface, tetherableNcmRegexs);
|
||||
}
|
||||
|
||||
/** Check whether no ui entitlement application is available.*/
|
||||
public boolean hasMobileHotspotProvisionApp() {
|
||||
return !TextUtils.isEmpty(provisioningAppNoUi);
|
||||
@@ -170,6 +177,7 @@ public class TetheringConfiguration {
|
||||
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
|
||||
dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
|
||||
dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
|
||||
dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs);
|
||||
|
||||
pw.print("isDunRequired: ");
|
||||
pw.println(isDunRequired);
|
||||
|
||||
@@ -19,6 +19,7 @@ android_test {
|
||||
certificate: "platform",
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
],
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.networkstack.tethering.tests.unit">
|
||||
|
||||
<uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
|
||||
|
||||
<application android:debuggable="true">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.connectivity.tethering;
|
||||
|
||||
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
|
||||
import static android.hardware.usb.UsbManager.USB_CONNECTED;
|
||||
import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM;
|
||||
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
|
||||
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
|
||||
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
|
||||
@@ -27,6 +28,7 @@ import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
|
||||
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
|
||||
import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
|
||||
import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
|
||||
import static android.net.TetheringManager.TETHERING_NCM;
|
||||
import static android.net.TetheringManager.TETHERING_USB;
|
||||
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||
@@ -151,6 +153,7 @@ public class TetheringTest {
|
||||
private static final String TEST_USB_IFNAME = "test_rndis0";
|
||||
private static final String TEST_WLAN_IFNAME = "test_wlan0";
|
||||
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
|
||||
private static final String TEST_NCM_IFNAME = "test_ncm0";
|
||||
private static final String TETHERING_NAME = "Tethering";
|
||||
|
||||
private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
|
||||
@@ -252,9 +255,11 @@ public class TetheringTest {
|
||||
ifName.equals(TEST_USB_IFNAME)
|
||||
|| ifName.equals(TEST_WLAN_IFNAME)
|
||||
|| ifName.equals(TEST_MOBILE_IFNAME)
|
||||
|| ifName.equals(TEST_P2P_IFNAME));
|
||||
|| ifName.equals(TEST_P2P_IFNAME)
|
||||
|| ifName.equals(TEST_NCM_IFNAME));
|
||||
final String[] ifaces = new String[] {
|
||||
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME};
|
||||
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME,
|
||||
TEST_NCM_IFNAME};
|
||||
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
|
||||
MacAddress.ALL_ZEROS_ADDRESS);
|
||||
}
|
||||
@@ -428,13 +433,16 @@ public class TetheringTest {
|
||||
.thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
|
||||
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
|
||||
.thenReturn(new String[0]);
|
||||
when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
|
||||
.thenReturn(new String[] { "test_ncm\\d" });
|
||||
when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
|
||||
when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
|
||||
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
|
||||
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_USB_IFNAME, TEST_P2P_IFNAME,
|
||||
TEST_NCM_IFNAME});
|
||||
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
|
||||
mInterfaceConfiguration = new InterfaceConfigurationParcel();
|
||||
mInterfaceConfiguration.flags = new String[0];
|
||||
@@ -524,11 +532,16 @@ public class TetheringTest {
|
||||
P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
|
||||
}
|
||||
|
||||
private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) {
|
||||
private void sendUsbBroadcast(boolean connected, boolean configured, boolean function,
|
||||
int type) {
|
||||
final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
|
||||
intent.putExtra(USB_CONNECTED, connected);
|
||||
intent.putExtra(USB_CONFIGURED, configured);
|
||||
intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction);
|
||||
if (type == TETHERING_USB) {
|
||||
intent.putExtra(USB_FUNCTION_RNDIS, function);
|
||||
} else {
|
||||
intent.putExtra(USB_FUNCTION_NCM, function);
|
||||
}
|
||||
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
|
||||
}
|
||||
|
||||
@@ -578,6 +591,15 @@ public class TetheringTest {
|
||||
verifyNoMoreInteractions(mWifiManager);
|
||||
}
|
||||
|
||||
private void prepareNcmTethering() {
|
||||
// Emulate startTethering(TETHERING_NCM) called
|
||||
mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null);
|
||||
mLooper.dispatchAll();
|
||||
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
|
||||
|
||||
mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
|
||||
}
|
||||
|
||||
private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
|
||||
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
|
||||
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
|
||||
@@ -600,7 +622,7 @@ public class TetheringTest {
|
||||
verifyNoMoreInteractions(mNetd);
|
||||
|
||||
// Pretend we then receive USB configured broadcast.
|
||||
sendUsbBroadcast(true, true, true);
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
mLooper.dispatchAll();
|
||||
// Now we should see the start of tethering mechanics (in this case:
|
||||
// tetherMatchingInterfaces() which starts by fetching all interfaces).
|
||||
@@ -691,7 +713,7 @@ public class TetheringTest {
|
||||
|
||||
private void runUsbTethering(UpstreamNetworkState upstreamState) {
|
||||
prepareUsbTethering(upstreamState);
|
||||
sendUsbBroadcast(true, true, true);
|
||||
sendUsbBroadcast(true, true, true, TETHERING_USB);
|
||||
mLooper.dispatchAll();
|
||||
}
|
||||
|
||||
@@ -814,6 +836,29 @@ public class TetheringTest {
|
||||
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
|
||||
}
|
||||
|
||||
private void runNcmTethering() {
|
||||
prepareNcmTethering();
|
||||
sendUsbBroadcast(true, true, true, TETHERING_NCM);
|
||||
mLooper.dispatchAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingNcmTethering() throws Exception {
|
||||
runNcmTethering();
|
||||
|
||||
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingNcmTethering_LegacyDhcp() {
|
||||
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
|
||||
true);
|
||||
sendConfigurationChanged();
|
||||
runNcmTethering();
|
||||
|
||||
verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
|
||||
workingLocalOnlyHotspotEnrichedApBroadcast(true);
|
||||
|
||||
Reference in New Issue
Block a user