Merge changes I8e806b3b,I5e8e4673,Id2a078da
* changes: Add CTS tests for EthernetNetworkProvider changes Move EthernetNetworkFactory to using the NetworkProvider API Improve waiting for interface added or removed
This commit is contained in:
@@ -31,10 +31,9 @@ import android.net.IpConfiguration.ProxySettings;
|
|||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.NetworkAgentConfig;
|
import android.net.NetworkAgentConfig;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkFactory;
|
|
||||||
import android.net.NetworkProvider;
|
import android.net.NetworkProvider;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.NetworkSpecifier;
|
import android.net.NetworkScore;
|
||||||
import android.net.ip.IIpClient;
|
import android.net.ip.IIpClient;
|
||||||
import android.net.ip.IpClientCallbacks;
|
import android.net.ip.IpClientCallbacks;
|
||||||
import android.net.ip.IpClientManager;
|
import android.net.ip.IpClientManager;
|
||||||
@@ -61,22 +60,19 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link NetworkFactory} that represents Ethernet networks.
|
* {@link NetworkProvider} that manages NetworkOffers for Ethernet networks.
|
||||||
*
|
|
||||||
* This class reports a static network score of 70 when it is tracking an interface and that
|
|
||||||
* interface's link is up, and a score of 0 otherwise.
|
|
||||||
*/
|
*/
|
||||||
public class EthernetNetworkFactory extends NetworkFactory {
|
public class EthernetNetworkFactory {
|
||||||
private final static String TAG = EthernetNetworkFactory.class.getSimpleName();
|
private final static String TAG = EthernetNetworkFactory.class.getSimpleName();
|
||||||
final static boolean DBG = true;
|
final static boolean DBG = true;
|
||||||
|
|
||||||
private final static int NETWORK_SCORE = 70;
|
|
||||||
private static final String NETWORK_TYPE = "Ethernet";
|
private static final String NETWORK_TYPE = "Ethernet";
|
||||||
|
|
||||||
private final ConcurrentHashMap<String, NetworkInterfaceState> mTrackingInterfaces =
|
private final ConcurrentHashMap<String, NetworkInterfaceState> mTrackingInterfaces =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
private final NetworkProvider mProvider;
|
||||||
final Dependencies mDeps;
|
final Dependencies mDeps;
|
||||||
|
|
||||||
public static class Dependencies {
|
public static class Dependencies {
|
||||||
@@ -111,54 +107,24 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public EthernetNetworkFactory(Handler handler, Context context) {
|
public EthernetNetworkFactory(Handler handler, Context context) {
|
||||||
this(handler, context, new Dependencies());
|
this(handler, context, new NetworkProvider(context, handler.getLooper(), TAG),
|
||||||
|
new Dependencies());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
EthernetNetworkFactory(Handler handler, Context context, Dependencies deps) {
|
EthernetNetworkFactory(Handler handler, Context context, NetworkProvider provider,
|
||||||
super(handler.getLooper(), context, NETWORK_TYPE, createDefaultNetworkCapabilities());
|
Dependencies deps) {
|
||||||
|
|
||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mProvider = provider;
|
||||||
mDeps = deps;
|
mDeps = deps;
|
||||||
|
|
||||||
setScoreFilter(NETWORK_SCORE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean acceptRequest(NetworkRequest request) {
|
* Registers the network provider with the system.
|
||||||
if (DBG) {
|
*/
|
||||||
Log.d(TAG, "acceptRequest, request: " + request);
|
public void register() {
|
||||||
}
|
mContext.getSystemService(ConnectivityManager.class).registerNetworkProvider(mProvider);
|
||||||
|
|
||||||
return networkForRequest(request) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void needNetworkFor(NetworkRequest networkRequest) {
|
|
||||||
NetworkInterfaceState network = networkForRequest(networkRequest);
|
|
||||||
|
|
||||||
if (network == null) {
|
|
||||||
Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++network.refCount == 1) {
|
|
||||||
network.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void releaseNetworkFor(NetworkRequest networkRequest) {
|
|
||||||
NetworkInterfaceState network = networkForRequest(networkRequest);
|
|
||||||
if (network == null) {
|
|
||||||
Log.e(TAG, "releaseNetworkFor, failed to get a network for " + networkRequest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (--network.refCount == 0) {
|
|
||||||
network.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -196,9 +162,8 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final NetworkInterfaceState iface = new NetworkInterfaceState(
|
final NetworkInterfaceState iface = new NetworkInterfaceState(
|
||||||
ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, getProvider(), mDeps);
|
ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, mProvider, mDeps);
|
||||||
mTrackingInterfaces.put(ifaceName, iface);
|
mTrackingInterfaces.put(ifaceName, iface);
|
||||||
updateCapabilityFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -239,7 +204,6 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
|
final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
|
||||||
iface.updateInterface(ipConfig, capabilities, listener);
|
iface.updateInterface(ipConfig, capabilities, listener);
|
||||||
mTrackingInterfaces.put(ifaceName, iface);
|
mTrackingInterfaces.put(ifaceName, iface);
|
||||||
updateCapabilityFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc,
|
private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc,
|
||||||
@@ -250,16 +214,6 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCapabilityFilter() {
|
|
||||||
NetworkCapabilities capabilitiesFilter = createDefaultNetworkCapabilities();
|
|
||||||
for (NetworkInterfaceState iface: mTrackingInterfaces.values()) {
|
|
||||||
capabilitiesFilter = mixInCapabilities(capabilitiesFilter, iface.mCapabilities);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter);
|
|
||||||
setCapabilityFilter(capabilitiesFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static NetworkCapabilities createDefaultNetworkCapabilities() {
|
private static NetworkCapabilities createDefaultNetworkCapabilities() {
|
||||||
return NetworkCapabilities.Builder
|
return NetworkCapabilities.Builder
|
||||||
.withoutDefaultCapabilities()
|
.withoutDefaultCapabilities()
|
||||||
@@ -270,11 +224,8 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
protected void removeInterface(String interfaceName) {
|
protected void removeInterface(String interfaceName) {
|
||||||
NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
|
NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
|
||||||
if (iface != null) {
|
if (iface != null) {
|
||||||
iface.maybeSendNetworkManagementCallbackForAbort();
|
iface.destroy();
|
||||||
iface.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCapabilityFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if state has been modified */
|
/** Returns true if state has been modified */
|
||||||
@@ -306,37 +257,6 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
return mTrackingInterfaces.containsKey(ifaceName);
|
return mTrackingInterfaces.containsKey(ifaceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NetworkInterfaceState networkForRequest(NetworkRequest request) {
|
|
||||||
String requestedIface = null;
|
|
||||||
|
|
||||||
NetworkSpecifier specifier = request.getNetworkSpecifier();
|
|
||||||
if (specifier instanceof EthernetNetworkSpecifier) {
|
|
||||||
requestedIface = ((EthernetNetworkSpecifier) specifier)
|
|
||||||
.getInterfaceName();
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkInterfaceState network = null;
|
|
||||||
if (!TextUtils.isEmpty(requestedIface)) {
|
|
||||||
NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface);
|
|
||||||
if (n != null && request.canBeSatisfiedBy(n.mCapabilities)) {
|
|
||||||
network = n;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (NetworkInterfaceState n : mTrackingInterfaces.values()) {
|
|
||||||
if (request.canBeSatisfiedBy(n.mCapabilities) && n.mLinkUp) {
|
|
||||||
network = n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DBG) {
|
|
||||||
Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network);
|
|
||||||
}
|
|
||||||
|
|
||||||
return network;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void maybeSendNetworkManagementCallback(
|
private static void maybeSendNetworkManagementCallback(
|
||||||
@Nullable final INetworkInterfaceOutcomeReceiver listener,
|
@Nullable final INetworkInterfaceOutcomeReceiver listener,
|
||||||
@Nullable final String iface,
|
@Nullable final String iface,
|
||||||
@@ -401,8 +321,6 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
ConnectivityManager.TYPE_NONE);
|
ConnectivityManager.TYPE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
long refCount = 0;
|
|
||||||
|
|
||||||
private class EthernetIpClientCallback extends IpClientCallbacks {
|
private class EthernetIpClientCallback extends IpClientCallbacks {
|
||||||
private final ConditionVariable mIpClientStartCv = new ConditionVariable(false);
|
private final ConditionVariable mIpClientStartCv = new ConditionVariable(false);
|
||||||
private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false);
|
private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false);
|
||||||
@@ -476,6 +394,9 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
private class EthernetNetworkOfferCallback implements NetworkProvider.NetworkOfferCallback {
|
private class EthernetNetworkOfferCallback implements NetworkProvider.NetworkOfferCallback {
|
||||||
@Override
|
@Override
|
||||||
public void onNetworkNeeded(@NonNull NetworkRequest request) {
|
public void onNetworkNeeded(@NonNull NetworkRequest request) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, String.format("%s: onNetworkNeeded for request: %s", name, request));
|
||||||
|
}
|
||||||
// When the network offer is first registered, onNetworkNeeded is called with all
|
// When the network offer is first registered, onNetworkNeeded is called with all
|
||||||
// existing requests.
|
// existing requests.
|
||||||
// ConnectivityService filters requests for us based on the NetworkCapabilities
|
// ConnectivityService filters requests for us based on the NetworkCapabilities
|
||||||
@@ -487,6 +408,10 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNetworkUnneeded(@NonNull NetworkRequest request) {
|
public void onNetworkUnneeded(@NonNull NetworkRequest request) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG,
|
||||||
|
String.format("%s: onNetworkUnneeded for request: %s", name, request));
|
||||||
|
}
|
||||||
mRequests.remove(request);
|
mRequests.remove(request);
|
||||||
if (mRequests.isEmpty()) {
|
if (mRequests.isEmpty()) {
|
||||||
// not currently serving any requests, stop the network.
|
// not currently serving any requests, stop the network.
|
||||||
@@ -529,9 +454,21 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
+ "transport type.");
|
+ "transport type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static NetworkScore getBestNetworkScore() {
|
||||||
|
return new NetworkScore.Builder().build();
|
||||||
|
}
|
||||||
|
|
||||||
private void setCapabilities(@NonNull final NetworkCapabilities capabilities) {
|
private void setCapabilities(@NonNull final NetworkCapabilities capabilities) {
|
||||||
mCapabilities = new NetworkCapabilities(capabilities);
|
mCapabilities = new NetworkCapabilities(capabilities);
|
||||||
mLegacyType = getLegacyType(mCapabilities);
|
mLegacyType = getLegacyType(mCapabilities);
|
||||||
|
|
||||||
|
if (mLinkUp) {
|
||||||
|
// registering a new network offer will update the existing one, not install a
|
||||||
|
// new one.
|
||||||
|
mNetworkProvider.registerNetworkOffer(getBestNetworkScore(),
|
||||||
|
new NetworkCapabilities(capabilities), cmd -> mHandler.post(cmd),
|
||||||
|
mNetworkOfferCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateInterface(@Nullable final IpConfiguration ipConfig,
|
void updateInterface(@Nullable final IpConfiguration ipConfig,
|
||||||
@@ -693,20 +630,21 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
mLinkUp = up;
|
mLinkUp = up;
|
||||||
|
|
||||||
if (!up) { // was up, goes down
|
if (!up) { // was up, goes down
|
||||||
// Send an abort on a provisioning request callback if necessary before stopping.
|
// retract network offer and stop IpClient.
|
||||||
maybeSendNetworkManagementCallbackForAbort();
|
destroy();
|
||||||
stop();
|
|
||||||
// If only setting the interface down, send a callback to signal completion.
|
// If only setting the interface down, send a callback to signal completion.
|
||||||
EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null);
|
EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null);
|
||||||
} else { // was down, goes up
|
} else { // was down, goes up
|
||||||
stop();
|
// register network offer
|
||||||
start(listener);
|
mNetworkProvider.registerNetworkOffer(getBestNetworkScore(),
|
||||||
|
new NetworkCapabilities(mCapabilities), (cmd) -> mHandler.post(cmd),
|
||||||
|
mNetworkOfferCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
private void stop() {
|
||||||
// Invalidate all previous start requests
|
// Invalidate all previous start requests
|
||||||
if (mIpClient != null) {
|
if (mIpClient != null) {
|
||||||
mIpClient.shutdown();
|
mIpClient.shutdown();
|
||||||
@@ -722,6 +660,13 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
mLinkProperties.clear();
|
mLinkProperties.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
mNetworkProvider.unregisterNetworkOffer(mNetworkOfferCallback);
|
||||||
|
maybeSendNetworkManagementCallbackForAbort();
|
||||||
|
stop();
|
||||||
|
mRequests.clear();
|
||||||
|
}
|
||||||
|
|
||||||
private static void provisionIpClient(@NonNull final IpClientManager ipClient,
|
private static void provisionIpClient(@NonNull final IpClientManager ipClient,
|
||||||
@NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) {
|
@NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) {
|
||||||
if (config.getProxySettings() == ProxySettings.STATIC ||
|
if (config.getProxySettings() == ProxySettings.STATIC ||
|
||||||
@@ -761,7 +706,6 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName() + "{ "
|
return getClass().getSimpleName() + "{ "
|
||||||
+ "refCount: " + refCount + ", "
|
|
||||||
+ "iface: " + name + ", "
|
+ "iface: " + name + ", "
|
||||||
+ "up: " + mLinkUp + ", "
|
+ "up: " + mLinkUp + ", "
|
||||||
+ "hwAddress: " + mHwAddress + ", "
|
+ "hwAddress: " + mHwAddress + ", "
|
||||||
@@ -774,7 +718,6 @@ public class EthernetNetworkFactory extends NetworkFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
|
void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
|
||||||
super.dump(fd, pw, args);
|
|
||||||
pw.println(getClass().getSimpleName());
|
pw.println(getClass().getSimpleName());
|
||||||
pw.println("Tracking interfaces:");
|
pw.println("Tracking interfaces:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
|
|||||||
@@ -19,9 +19,16 @@ import android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS
|
|||||||
import android.Manifest.permission.MANAGE_TEST_NETWORKS
|
import android.Manifest.permission.MANAGE_TEST_NETWORKS
|
||||||
import android.Manifest.permission.NETWORK_SETTINGS
|
import android.Manifest.permission.NETWORK_SETTINGS
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.EthernetNetworkSpecifier
|
||||||
import android.net.InetAddresses
|
import android.net.InetAddresses
|
||||||
import android.net.IpConfiguration
|
import android.net.IpConfiguration
|
||||||
import android.net.MacAddress
|
import android.net.MacAddress
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
|
||||||
|
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
|
||||||
|
import android.net.NetworkCapabilities.TRANSPORT_TEST
|
||||||
|
import android.net.NetworkRequest
|
||||||
import android.net.TestNetworkInterface
|
import android.net.TestNetworkInterface
|
||||||
import android.net.TestNetworkManager
|
import android.net.TestNetworkManager
|
||||||
import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.InterfaceStateChanged
|
import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.InterfaceStateChanged
|
||||||
@@ -41,10 +48,14 @@ import com.android.networkstack.apishim.common.EthernetManagerShim.ROLE_NONE
|
|||||||
import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_ABSENT
|
import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_ABSENT
|
||||||
import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_LINK_DOWN
|
import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_LINK_DOWN
|
||||||
import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_LINK_UP
|
import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_LINK_UP
|
||||||
|
import com.android.testutils.anyNetwork
|
||||||
import com.android.testutils.DevSdkIgnoreRule
|
import com.android.testutils.DevSdkIgnoreRule
|
||||||
|
import com.android.testutils.RecorderCallback.CallbackEntry.Available
|
||||||
|
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
|
||||||
import com.android.testutils.RouterAdvertisementResponder
|
import com.android.testutils.RouterAdvertisementResponder
|
||||||
import com.android.testutils.SC_V2
|
import com.android.testutils.SC_V2
|
||||||
import com.android.testutils.TapPacketReader
|
import com.android.testutils.TapPacketReader
|
||||||
|
import com.android.testutils.TestableNetworkCallback
|
||||||
import com.android.testutils.runAsShell
|
import com.android.testutils.runAsShell
|
||||||
import com.android.testutils.waitForIdle
|
import com.android.testutils.waitForIdle
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@@ -64,6 +75,11 @@ private const val TIMEOUT_MS = 1000L
|
|||||||
private const val NO_CALLBACK_TIMEOUT_MS = 200L
|
private const val NO_CALLBACK_TIMEOUT_MS = 200L
|
||||||
private val DEFAULT_IP_CONFIGURATION = IpConfiguration(IpConfiguration.IpAssignment.DHCP,
|
private val DEFAULT_IP_CONFIGURATION = IpConfiguration(IpConfiguration.IpAssignment.DHCP,
|
||||||
IpConfiguration.ProxySettings.NONE, null, null)
|
IpConfiguration.ProxySettings.NONE, null, null)
|
||||||
|
private val ETH_REQUEST: NetworkRequest = NetworkRequest.Builder()
|
||||||
|
.addTransportType(TRANSPORT_TEST)
|
||||||
|
.addTransportType(TRANSPORT_ETHERNET)
|
||||||
|
.removeCapability(NET_CAPABILITY_TRUSTED)
|
||||||
|
.build()
|
||||||
|
|
||||||
@AppModeFull(reason = "Instant apps can't access EthernetManager")
|
@AppModeFull(reason = "Instant apps can't access EthernetManager")
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@@ -74,10 +90,12 @@ class EthernetManagerTest {
|
|||||||
|
|
||||||
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
|
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
|
||||||
private val em by lazy { EthernetManagerShimImpl.newInstance(context) }
|
private val em by lazy { EthernetManagerShimImpl.newInstance(context) }
|
||||||
|
private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
|
||||||
|
|
||||||
private val ifaceListener = EthernetStateListener()
|
private val ifaceListener = EthernetStateListener()
|
||||||
private val createdIfaces = ArrayList<EthernetTestInterface>()
|
private val createdIfaces = ArrayList<EthernetTestInterface>()
|
||||||
private val addedListeners = ArrayList<EthernetStateListener>()
|
private val addedListeners = ArrayList<EthernetStateListener>()
|
||||||
|
private val networkRequests = ArrayList<TestableNetworkCallback>()
|
||||||
|
|
||||||
private class EthernetTestInterface(
|
private class EthernetTestInterface(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -177,10 +195,12 @@ class EthernetManagerTest {
|
|||||||
setIncludeTestInterfaces(false)
|
setIncludeTestInterfaces(false)
|
||||||
for (iface in createdIfaces) {
|
for (iface in createdIfaces) {
|
||||||
iface.destroy()
|
iface.destroy()
|
||||||
|
ifaceListener.eventuallyExpect(iface, STATE_ABSENT, ROLE_NONE)
|
||||||
}
|
}
|
||||||
for (listener in addedListeners) {
|
for (listener in addedListeners) {
|
||||||
em.removeInterfaceStateListener(listener)
|
em.removeInterfaceStateListener(listener)
|
||||||
}
|
}
|
||||||
|
networkRequests.forEach { cm.unregisterNetworkCallback(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addInterfaceStateListener(listener: EthernetStateListener) {
|
private fun addInterfaceStateListener(listener: EthernetStateListener) {
|
||||||
@@ -195,7 +215,11 @@ class EthernetManagerTest {
|
|||||||
context,
|
context,
|
||||||
Handler(Looper.getMainLooper())
|
Handler(Looper.getMainLooper())
|
||||||
).also { createdIfaces.add(it) }
|
).also { createdIfaces.add(it) }
|
||||||
ifaceListener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_CLIENT)
|
with(ifaceListener) {
|
||||||
|
// when an interface comes up, we should always see a down cb before an up cb.
|
||||||
|
eventuallyExpect(iface, STATE_LINK_DOWN, ROLE_CLIENT)
|
||||||
|
expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
|
||||||
|
}
|
||||||
return iface
|
return iface
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,8 +232,37 @@ class EthernetManagerTest {
|
|||||||
private fun removeInterface(iface: EthernetTestInterface) {
|
private fun removeInterface(iface: EthernetTestInterface) {
|
||||||
iface.destroy()
|
iface.destroy()
|
||||||
createdIfaces.remove(iface)
|
createdIfaces.remove(iface)
|
||||||
|
ifaceListener.eventuallyExpect(iface, STATE_ABSENT, ROLE_NONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun requestNetwork(request: NetworkRequest): TestableNetworkCallback {
|
||||||
|
return TestableNetworkCallback().also {
|
||||||
|
cm.requestNetwork(request, it)
|
||||||
|
networkRequests.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun releaseNetwork(cb: TestableNetworkCallback) {
|
||||||
|
cm.unregisterNetworkCallback(cb)
|
||||||
|
networkRequests.remove(cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NetworkRequest.createCopyWithEthernetSpecifier(ifaceName: String) =
|
||||||
|
NetworkRequest.Builder(NetworkRequest(ETH_REQUEST))
|
||||||
|
.setNetworkSpecifier(EthernetNetworkSpecifier(ifaceName)).build()
|
||||||
|
|
||||||
|
// It can take multiple seconds for the network to become available.
|
||||||
|
private fun TestableNetworkCallback.expectAvailable() =
|
||||||
|
expectCallback<Available>(anyNetwork(), 5000/*ms timeout*/).network
|
||||||
|
|
||||||
|
// b/233534110: eventuallyExpect<Lost>() does not advance ReadHead, use
|
||||||
|
// eventuallyExpect(Lost::class) instead.
|
||||||
|
private fun TestableNetworkCallback.eventuallyExpectLost(n: Network? = null) =
|
||||||
|
eventuallyExpect(Lost::class, TIMEOUT_MS) { n?.equals(it.network) ?: true }
|
||||||
|
|
||||||
|
private fun TestableNetworkCallback.assertNotLost(n: Network? = null) =
|
||||||
|
assertNoCallbackThat() { it is Lost && (n?.equals(it.network) ?: true) }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCallbacks() {
|
fun testCallbacks() {
|
||||||
// If an interface exists when the callback is registered, it is reported on registration.
|
// If an interface exists when the callback is registered, it is reported on registration.
|
||||||
@@ -289,4 +342,105 @@ class EthernetManagerTest {
|
|||||||
|
|
||||||
removeInterface(iface2)
|
removeInterface(iface2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNetworkRequest_withSingleExistingInterface() {
|
||||||
|
setIncludeTestInterfaces(true)
|
||||||
|
createInterface()
|
||||||
|
|
||||||
|
// install a listener which will later be used to verify the Lost callback
|
||||||
|
val listenerCb = TestableNetworkCallback()
|
||||||
|
cm.registerNetworkCallback(ETH_REQUEST, listenerCb)
|
||||||
|
networkRequests.add(listenerCb)
|
||||||
|
|
||||||
|
val cb = requestNetwork(ETH_REQUEST)
|
||||||
|
val network = cb.expectAvailable()
|
||||||
|
|
||||||
|
cb.assertNotLost()
|
||||||
|
releaseNetwork(cb)
|
||||||
|
listenerCb.eventuallyExpectLost(network)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNetworkRequest_beforeSingleInterfaceIsUp() {
|
||||||
|
setIncludeTestInterfaces(true)
|
||||||
|
|
||||||
|
val cb = requestNetwork(ETH_REQUEST)
|
||||||
|
|
||||||
|
// bring up interface after network has been requested
|
||||||
|
val iface = createInterface()
|
||||||
|
val network = cb.expectAvailable()
|
||||||
|
|
||||||
|
// remove interface before network request has been removed
|
||||||
|
cb.assertNotLost()
|
||||||
|
removeInterface(iface)
|
||||||
|
cb.eventuallyExpectLost()
|
||||||
|
|
||||||
|
releaseNetwork(cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNetworkRequest_withMultipleInterfaces() {
|
||||||
|
setIncludeTestInterfaces(true)
|
||||||
|
|
||||||
|
val iface1 = createInterface()
|
||||||
|
val iface2 = createInterface()
|
||||||
|
|
||||||
|
val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.interfaceName))
|
||||||
|
|
||||||
|
val network = cb.expectAvailable()
|
||||||
|
cb.expectCapabilitiesThat(network) {
|
||||||
|
it.networkSpecifier == EthernetNetworkSpecifier(iface2.interfaceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeInterface(iface1)
|
||||||
|
cb.assertNotLost()
|
||||||
|
removeInterface(iface2)
|
||||||
|
cb.eventuallyExpectLost()
|
||||||
|
|
||||||
|
releaseNetwork(cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNetworkRequest_withInterfaceBeingReplaced() {
|
||||||
|
setIncludeTestInterfaces(true)
|
||||||
|
val iface1 = createInterface()
|
||||||
|
|
||||||
|
val cb = requestNetwork(ETH_REQUEST)
|
||||||
|
val network = cb.expectAvailable()
|
||||||
|
|
||||||
|
// create another network and verify the request sticks to the current network
|
||||||
|
val iface2 = createInterface()
|
||||||
|
cb.assertNotLost()
|
||||||
|
|
||||||
|
// remove iface1 and verify the request brings up iface2
|
||||||
|
removeInterface(iface1)
|
||||||
|
cb.eventuallyExpectLost(network)
|
||||||
|
val network2 = cb.expectAvailable()
|
||||||
|
|
||||||
|
releaseNetwork(cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNetworkRequest_withMultipleInterfacesAndRequests() {
|
||||||
|
setIncludeTestInterfaces(true)
|
||||||
|
val iface1 = createInterface()
|
||||||
|
val iface2 = createInterface()
|
||||||
|
|
||||||
|
val cb1 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface1.interfaceName))
|
||||||
|
val cb2 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.interfaceName))
|
||||||
|
val cb3 = requestNetwork(ETH_REQUEST)
|
||||||
|
|
||||||
|
cb1.expectAvailable()
|
||||||
|
cb2.expectAvailable()
|
||||||
|
cb3.expectAvailable()
|
||||||
|
|
||||||
|
cb1.assertNotLost()
|
||||||
|
cb2.assertNotLost()
|
||||||
|
cb3.assertNotLost()
|
||||||
|
|
||||||
|
releaseNetwork(cb1)
|
||||||
|
releaseNetwork(cb2)
|
||||||
|
releaseNetwork(cb3)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import static org.mockito.ArgumentMatchers.same;
|
|||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -51,6 +50,7 @@ import android.net.Network;
|
|||||||
import android.net.NetworkAgentConfig;
|
import android.net.NetworkAgentConfig;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkProvider;
|
import android.net.NetworkProvider;
|
||||||
|
import android.net.NetworkProvider.NetworkOfferCallback;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.StaticIpConfiguration;
|
import android.net.StaticIpConfiguration;
|
||||||
import android.net.ip.IpClientCallbacks;
|
import android.net.ip.IpClientCallbacks;
|
||||||
@@ -99,6 +99,7 @@ public class EthernetNetworkFactoryTest {
|
|||||||
@Mock private EthernetNetworkAgent mNetworkAgent;
|
@Mock private EthernetNetworkAgent mNetworkAgent;
|
||||||
@Mock private InterfaceParams mInterfaceParams;
|
@Mock private InterfaceParams mInterfaceParams;
|
||||||
@Mock private Network mMockNetwork;
|
@Mock private Network mMockNetwork;
|
||||||
|
@Mock private NetworkProvider mNetworkProvider;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
@@ -112,7 +113,7 @@ public class EthernetNetworkFactoryTest {
|
|||||||
private void initEthernetNetworkFactory() {
|
private void initEthernetNetworkFactory() {
|
||||||
mLooper = new TestLooper();
|
mLooper = new TestLooper();
|
||||||
mHandler = new Handler(mLooper.getLooper());
|
mHandler = new Handler(mLooper.getLooper());
|
||||||
mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mDeps);
|
mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mNetworkProvider, mDeps);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupNetworkAgentMock() {
|
private void setupNetworkAgentMock() {
|
||||||
@@ -239,9 +240,16 @@ public class EthernetNetworkFactoryTest {
|
|||||||
mNetFactory.addInterface(iface, HW_ADDR, ipConfig,
|
mNetFactory.addInterface(iface, HW_ADDR, ipConfig,
|
||||||
createInterfaceCapsBuilder(transportType).build());
|
createInterfaceCapsBuilder(transportType).build());
|
||||||
assertTrue(mNetFactory.updateInterfaceLinkState(iface, true, NULL_LISTENER));
|
assertTrue(mNetFactory.updateInterfaceLinkState(iface, true, NULL_LISTENER));
|
||||||
|
|
||||||
|
ArgumentCaptor<NetworkOfferCallback> captor = ArgumentCaptor.forClass(
|
||||||
|
NetworkOfferCallback.class);
|
||||||
|
verify(mNetworkProvider).registerNetworkOffer(any(), any(), any(), captor.capture());
|
||||||
|
captor.getValue().onNetworkNeeded(createDefaultRequest());
|
||||||
|
|
||||||
verifyStart(ipConfig);
|
verifyStart(ipConfig);
|
||||||
clearInvocations(mDeps);
|
clearInvocations(mDeps);
|
||||||
clearInvocations(mIpClient);
|
clearInvocations(mIpClient);
|
||||||
|
clearInvocations(mNetworkProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a provisioned interface
|
// creates a provisioned interface
|
||||||
@@ -281,28 +289,14 @@ public class EthernetNetworkFactoryTest {
|
|||||||
// To create an unprovisioned interface, provision and then "stop" it, i.e. stop its
|
// To create an unprovisioned interface, provision and then "stop" it, i.e. stop its
|
||||||
// NetworkAgent and IpClient. One way this can be done is by provisioning an interface and
|
// NetworkAgent and IpClient. One way this can be done is by provisioning an interface and
|
||||||
// then calling onNetworkUnwanted.
|
// then calling onNetworkUnwanted.
|
||||||
createAndVerifyProvisionedInterface(iface);
|
mNetFactory.addInterface(iface, HW_ADDR, createDefaultIpConfig(),
|
||||||
|
createInterfaceCapsBuilder(NetworkCapabilities.TRANSPORT_ETHERNET).build());
|
||||||
mNetworkAgent.getCallbacks().onNetworkUnwanted();
|
assertTrue(mNetFactory.updateInterfaceLinkState(iface, true, NULL_LISTENER));
|
||||||
mLooper.dispatchAll();
|
|
||||||
verifyStop();
|
|
||||||
|
|
||||||
clearInvocations(mIpClient);
|
clearInvocations(mIpClient);
|
||||||
clearInvocations(mNetworkAgent);
|
clearInvocations(mNetworkAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAcceptRequest() throws Exception {
|
|
||||||
initEthernetNetworkFactory();
|
|
||||||
createInterfaceUndergoingProvisioning(TEST_IFACE);
|
|
||||||
assertTrue(mNetFactory.acceptRequest(createDefaultRequest()));
|
|
||||||
|
|
||||||
NetworkRequest wifiRequest = createDefaultRequestBuilder()
|
|
||||||
.removeTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
|
||||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
|
|
||||||
assertFalse(mNetFactory.acceptRequest(wifiRequest));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception {
|
public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception {
|
||||||
initEthernetNetworkFactory();
|
initEthernetNetworkFactory();
|
||||||
@@ -377,36 +371,6 @@ public class EthernetNetworkFactoryTest {
|
|||||||
listener.expectOnError();
|
listener.expectOnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNeedNetworkForOnProvisionedInterface() throws Exception {
|
|
||||||
initEthernetNetworkFactory();
|
|
||||||
createAndVerifyProvisionedInterface(TEST_IFACE);
|
|
||||||
mNetFactory.needNetworkFor(createDefaultRequest());
|
|
||||||
verify(mIpClient, never()).startProvisioning(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNeedNetworkForOnUnprovisionedInterface() throws Exception {
|
|
||||||
initEthernetNetworkFactory();
|
|
||||||
createUnprovisionedInterface(TEST_IFACE);
|
|
||||||
mNetFactory.needNetworkFor(createDefaultRequest());
|
|
||||||
verify(mIpClient).startProvisioning(any());
|
|
||||||
|
|
||||||
triggerOnProvisioningSuccess();
|
|
||||||
verifyNetworkAgentRegistersAndConnects();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNeedNetworkForOnInterfaceUndergoingProvisioning() throws Exception {
|
|
||||||
initEthernetNetworkFactory();
|
|
||||||
createInterfaceUndergoingProvisioning(TEST_IFACE);
|
|
||||||
mNetFactory.needNetworkFor(createDefaultRequest());
|
|
||||||
verify(mIpClient, never()).startProvisioning(any());
|
|
||||||
|
|
||||||
triggerOnProvisioningSuccess();
|
|
||||||
verifyNetworkAgentRegistersAndConnects();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProvisioningLoss() throws Exception {
|
public void testProvisioningLoss() throws Exception {
|
||||||
initEthernetNetworkFactory();
|
initEthernetNetworkFactory();
|
||||||
@@ -440,31 +404,6 @@ public class EthernetNetworkFactoryTest {
|
|||||||
verify(mIpClient, never()).startProvisioning(any());
|
verify(mIpClient, never()).startProvisioning(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIpClientIsNotStartedWhenLinkIsDown() throws Exception {
|
|
||||||
initEthernetNetworkFactory();
|
|
||||||
createUnprovisionedInterface(TEST_IFACE);
|
|
||||||
mNetFactory.updateInterfaceLinkState(TEST_IFACE, false, NULL_LISTENER);
|
|
||||||
|
|
||||||
mNetFactory.needNetworkFor(createDefaultRequest());
|
|
||||||
|
|
||||||
verify(mDeps, never()).makeIpClient(any(), any(), any());
|
|
||||||
|
|
||||||
// BUG(b/191854824): requesting a network with a specifier (Android Auto use case) should
|
|
||||||
// not start an IpClient when the link is down, but fixing this may make matters worse by
|
|
||||||
// tiggering b/197548738.
|
|
||||||
NetworkRequest specificNetRequest = new NetworkRequest.Builder()
|
|
||||||
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
|
||||||
.setNetworkSpecifier(new EthernetNetworkSpecifier(TEST_IFACE))
|
|
||||||
.build();
|
|
||||||
mNetFactory.needNetworkFor(specificNetRequest);
|
|
||||||
mNetFactory.releaseNetworkFor(specificNetRequest);
|
|
||||||
|
|
||||||
mNetFactory.updateInterfaceLinkState(TEST_IFACE, true, NULL_LISTENER);
|
|
||||||
// TODO: change to once when b/191854824 is fixed.
|
|
||||||
verify(mDeps, times(2)).makeIpClient(any(), eq(TEST_IFACE), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLinkPropertiesChanged() throws Exception {
|
public void testLinkPropertiesChanged() throws Exception {
|
||||||
initEthernetNetworkFactory();
|
initEthernetNetworkFactory();
|
||||||
|
|||||||
Reference in New Issue
Block a user