Merge changes I34842acd,Icc6c4d6b

* changes:
  Add support for Ethernet tethering
  Local Tethering with ncm interface
This commit is contained in:
Lorenzo Colitti
2020-01-28 10:43:33 +00:00
committed by Gerrit Code Review
14 changed files with 250 additions and 13 deletions

View File

@@ -1577,6 +1577,7 @@ package android.content {
field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final String NETD_SERVICE = "netd";
@@ -3285,10 +3286,12 @@ package android.hardware.usb {
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
field public static final long FUNCTION_NCM = 1024L; // 0x400L
field public static final long FUNCTION_NONE = 0L; // 0x0L
field public static final long FUNCTION_RNDIS = 32L; // 0x20L
field public static final String USB_CONFIGURED = "configured";
field public static final String USB_CONNECTED = "connected";
field public static final String USB_FUNCTION_NCM = "ncm";
field public static final String USB_FUNCTION_RNDIS = "rndis";
}
@@ -4380,6 +4383,19 @@ package android.net {
method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network);
}
public class EthernetManager {
method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback);
}
public static interface EthernetManager.TetheredInterfaceCallback {
method public void onAvailable(@NonNull String);
method public void onUnavailable();
}
public static class EthernetManager.TetheredInterfaceRequest {
method public void release();
}
public class InvalidPacketException extends java.lang.Exception {
ctor public InvalidPacketException(int);
field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
@@ -4753,7 +4769,9 @@ package android.net {
field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_ETHERNET = 5; // 0x5
field public static final int TETHERING_INVALID = -1; // 0xffffffff
field public static final int TETHERING_NCM = 4; // 0x4
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TETHERING_WIFI_P2P = 3; // 0x3

View File

@@ -653,6 +653,7 @@ package android.content {
method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String NETWORK_STACK_SERVICE = "network_stack";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String ROLLBACK_SERVICE = "rollback";
@@ -1392,6 +1393,19 @@ package android.net {
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
}
public class EthernetManager {
method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback);
}
public static interface EthernetManager.TetheredInterfaceCallback {
method public void onAvailable(@NonNull String);
method public void onUnavailable();
}
public static class EthernetManager.TetheredInterfaceRequest {
method public void release();
}
public final class IpPrefix implements android.os.Parcelable {
ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public IpPrefix(@NonNull String);
@@ -1540,7 +1554,9 @@ package android.net {
field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_ETHERNET = 5; // 0x5
field public static final int TETHERING_INVALID = -1; // 0xffffffff
field public static final int TETHERING_NCM = 4; // 0x4
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TETHERING_WIFI_P2P = 3; // 0x3

View File

@@ -4022,16 +4022,16 @@ public abstract class Context {
public static final String LOWPAN_SERVICE = "lowpan";
/**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.EthernetManager} for handling management of
* Ethernet access.
* Use with {@link #getSystemService(String)} to retrieve a {@link android.net.EthernetManager}
* for handling management of Ethernet access.
*
* @see #getSystemService(String)
* @see android.net.EthernetManager
*
* @hide
*/
@UnsupportedAppUsage
@SystemApi
@TestApi
public static final String ETHERNET_SERVICE = "ethernet";
/**

View File

@@ -261,6 +261,15 @@ public class UsbManager {
*/
public static final String USB_FUNCTION_ACCESSORY = "accessory";
/**
* Name of the NCM USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*
* {@hide}
*/
@SystemApi
public static final String USB_FUNCTION_NCM = "ncm";
/**
* Name of extra for {@link #ACTION_USB_PORT_CHANGED}
* containing the {@link UsbPort} object for the port.
@@ -367,8 +376,15 @@ public class UsbManager {
*/
public static final long FUNCTION_ADB = GadgetFunction.ADB;
/**
* Code for the ncm source usb function.
* {@hide}
*/
@SystemApi
public static final long FUNCTION_NCM = 1 << 10;
private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
| FUNCTION_MIDI;
| FUNCTION_MIDI | FUNCTION_NCM;
private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
@@ -380,6 +396,7 @@ public class UsbManager {
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM);
}
private final Context mContext;
@@ -935,6 +952,9 @@ public class UsbManager {
if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
}
if ((functions & FUNCTION_NCM) != 0) {
joiner.add(UsbManager.USB_FUNCTION_NCM);
}
if ((functions & FUNCTION_ADB) != 0) {
joiner.add(UsbManager.USB_FUNCTION_ADB);
}

View File

@@ -17,7 +17,9 @@
package android.net;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Handler;
@@ -32,6 +34,8 @@ import java.util.Objects;
*
* @hide
*/
@SystemApi
@TestApi
@SystemService(Context.ETHERNET_SERVICE)
public class EthernetManager {
private static final String TAG = "EthernetManager";
@@ -62,12 +66,14 @@ public class EthernetManager {
/**
* A listener interface to receive notification on changes in Ethernet.
* @hide
*/
public interface Listener {
/**
* Called when Ethernet port's availability is changed.
* @param iface Ethernet interface name
* @param isAvailable {@code true} if Ethernet port exists.
* @hide
*/
@UnsupportedAppUsage
void onAvailabilityChanged(String iface, boolean isAvailable);
@@ -78,6 +84,7 @@ public class EthernetManager {
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#ETHERNET_SERVICE Context.ETHERNET_SERVICE}.
* @hide
*/
public EthernetManager(Context context, IEthernetManager service) {
mContext = context;
@@ -87,6 +94,7 @@ public class EthernetManager {
/**
* Get Ethernet configuration.
* @return the Ethernet Configuration, contained in {@link IpConfiguration}.
* @hide
*/
@UnsupportedAppUsage
public IpConfiguration getConfiguration(String iface) {
@@ -99,6 +107,7 @@ public class EthernetManager {
/**
* Set Ethernet configuration.
* @hide
*/
@UnsupportedAppUsage
public void setConfiguration(String iface, IpConfiguration config) {
@@ -111,6 +120,7 @@ public class EthernetManager {
/**
* Indicates whether the system currently has one or more Ethernet interfaces.
* @hide
*/
@UnsupportedAppUsage
public boolean isAvailable() {
@@ -121,6 +131,7 @@ public class EthernetManager {
* Indicates whether the system has given interface.
*
* @param iface Ethernet interface name
* @hide
*/
@UnsupportedAppUsage
public boolean isAvailable(String iface) {
@@ -135,6 +146,7 @@ public class EthernetManager {
* Adds a listener.
* @param listener A {@link Listener} to add.
* @throws IllegalArgumentException If the listener is null.
* @hide
*/
@UnsupportedAppUsage
public void addListener(Listener listener) {
@@ -153,6 +165,7 @@ public class EthernetManager {
/**
* Returns an array of available Ethernet interface names.
* @hide
*/
@UnsupportedAppUsage
public String[] getAvailableInterfaces() {
@@ -167,6 +180,7 @@ public class EthernetManager {
* Removes a listener.
* @param listener A {@link Listener} to remove.
* @throws IllegalArgumentException If the listener is null.
* @hide
*/
@UnsupportedAppUsage
public void removeListener(Listener listener) {

View File

@@ -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;

View File

@@ -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.*" -->

View File

@@ -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"/>

View File

@@ -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

View File

@@ -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>();

View File

@@ -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);

View File

@@ -19,6 +19,7 @@ android_test {
certificate: "platform",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
test_suites: [
"device-tests",

View File

@@ -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>

View File

@@ -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);