Updates so Network Mgmt Callbacks Are Sent

Updates so that network management API updates in the ethernet stack can
handle concurrent requests correctly in regards to notifying caller
supplied callbacks.

Bug: 210485380
Test: atest EthernetServiceTests
Change-Id: I8073251b2c63309da031c2b1c98d61614deadfe8
This commit is contained in:
James Mattis
2022-01-13 18:36:24 -08:00
parent 2730f2ea2d
commit 724a0aea08
2 changed files with 221 additions and 68 deletions

View File

@@ -22,10 +22,12 @@ import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.EthernetNetworkSpecifier; import android.net.EthernetNetworkSpecifier;
import android.net.IInternalNetworkManagementListener; import android.net.IInternalNetworkManagementListener;
import android.net.InternalNetworkManagementException;
import android.net.IpConfiguration; import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings; import android.net.IpConfiguration.ProxySettings;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgentConfig; import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkFactory; import android.net.NetworkFactory;
@@ -203,15 +205,10 @@ public class EthernetNetworkFactory extends NetworkFactory {
@Nullable final IInternalNetworkManagementListener listener) { @Nullable final IInternalNetworkManagementListener listener) {
enforceInterfaceIsTracked(ifaceName); enforceInterfaceIsTracked(ifaceName);
final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
// TODO: The listener will have issues if called in quick succession for the same interface iface.updateInterface(ipConfig, capabilities, listener);
// before the IP layer restarts. Update the listener logic to address multiple successive
// calls for a particular interface.
iface.mNetworkManagementListener = listener;
if (iface.updateInterface(ipConfig, capabilities)) {
mTrackingInterfaces.put(ifaceName, iface); mTrackingInterfaces.put(ifaceName, iface);
updateCapabilityFilter(); updateCapabilityFilter();
} }
}
private void enforceInterfaceIsTracked(@NonNull final String ifaceName) { private void enforceInterfaceIsTracked(@NonNull final String ifaceName) {
if (!hasInterface(ifaceName)) { if (!hasInterface(ifaceName)) {
@@ -247,6 +244,7 @@ public class EthernetNetworkFactory extends NetworkFactory {
void removeInterface(String interfaceName) { void removeInterface(String interfaceName) {
NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
if (iface != null) { if (iface != null) {
iface.maybeSendNetworkManagementCallbackForAbort();
iface.stop(); iface.stop();
} }
@@ -302,6 +300,21 @@ public class EthernetNetworkFactory extends NetworkFactory {
return network; return network;
} }
private static void maybeSendNetworkManagementCallback(
@Nullable final IInternalNetworkManagementListener listener,
@Nullable final Network network,
@Nullable final InternalNetworkManagementException e) {
if (null == listener) {
return;
}
try {
listener.onComplete(network, e);
} catch (RemoteException re) {
Log.e(TAG, "Can't send onComplete for network management callback", re);
}
}
@VisibleForTesting @VisibleForTesting
static class NetworkInterfaceState { static class NetworkInterfaceState {
final String name; final String name;
@@ -320,8 +333,7 @@ public class EthernetNetworkFactory extends NetworkFactory {
private volatile @Nullable IpClientManager mIpClient; private volatile @Nullable IpClientManager mIpClient;
private @NonNull NetworkCapabilities mCapabilities; private @NonNull NetworkCapabilities mCapabilities;
private @Nullable IpClientCallbacksImpl mIpClientCallback; private @Nullable EthernetIpClientCallback mIpClientCallback;
private @Nullable IInternalNetworkManagementListener mNetworkManagementListener;
private @Nullable EthernetNetworkAgent mNetworkAgent; private @Nullable EthernetNetworkAgent mNetworkAgent;
private @Nullable IpConfiguration mIpConfig; private @Nullable IpConfiguration mIpConfig;
@@ -348,9 +360,14 @@ public class EthernetNetworkFactory extends NetworkFactory {
long refCount = 0; long refCount = 0;
private class IpClientCallbacksImpl 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);
@Nullable IInternalNetworkManagementListener mNetworkManagementListener;
EthernetIpClientCallback(@Nullable final IInternalNetworkManagementListener listener) {
mNetworkManagementListener = listener;
}
@Override @Override
public void onIpClientCreated(IIpClient ipClient) { public void onIpClientCreated(IIpClient ipClient) {
@@ -368,12 +385,12 @@ public class EthernetNetworkFactory extends NetworkFactory {
@Override @Override
public void onProvisioningSuccess(LinkProperties newLp) { public void onProvisioningSuccess(LinkProperties newLp) {
mHandler.post(() -> onIpLayerStarted(newLp)); mHandler.post(() -> onIpLayerStarted(newLp, mNetworkManagementListener));
} }
@Override @Override
public void onProvisioningFailure(LinkProperties newLp) { public void onProvisioningFailure(LinkProperties newLp) {
mHandler.post(() -> onIpLayerStopped(newLp)); mHandler.post(() -> onIpLayerStopped(mNetworkManagementListener));
} }
@Override @Override
@@ -431,30 +448,25 @@ public class EthernetNetworkFactory extends NetworkFactory {
mLegacyType = getLegacyType(mCapabilities); mLegacyType = getLegacyType(mCapabilities);
} }
boolean updateInterface(@NonNull final IpConfiguration ipConfig, void updateInterface(@NonNull final IpConfiguration ipConfig,
@Nullable final NetworkCapabilities capabilities) { @Nullable final NetworkCapabilities capabilities,
final boolean shouldUpdateIpConfig = !Objects.equals(mIpConfig, ipConfig); @Nullable final IInternalNetworkManagementListener listener) {
final boolean shouldUpdateCapabilities = null != capabilities
&& !Objects.equals(mCapabilities, capabilities);
if (DBG) { if (DBG) {
Log.d(TAG, "updateInterface, iface: " + name Log.d(TAG, "updateInterface, iface: " + name
+ ", shouldUpdateIpConfig: " + shouldUpdateIpConfig
+ ", shouldUpdateCapabilities: " + shouldUpdateCapabilities
+ ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig
+ ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities
+ ", listener: " + listener
); );
} }
if (shouldUpdateIpConfig) { mIpConfig = ipConfig; }; mIpConfig = ipConfig;
if (shouldUpdateCapabilities) { setCapabilities(capabilities); }; setCapabilities(capabilities);
if (shouldUpdateIpConfig || shouldUpdateCapabilities) { // Send an abort callback if a request is filed before the previous one has completed.
maybeSendNetworkManagementCallbackForAbort();
// TODO: Update this logic to only do a restart if required. Although a restart may // TODO: Update this logic to only do a restart if required. Although a restart may
// be required due to the capabilities or ipConfiguration values, not all // be required due to the capabilities or ipConfiguration values, not all
// capabilities changes require a restart. // capabilities changes require a restart.
restart(); restart(listener);
return true;
}
return false;
} }
boolean isRestricted() { boolean isRestricted() {
@@ -462,6 +474,10 @@ public class EthernetNetworkFactory extends NetworkFactory {
} }
private void start() { private void start() {
start(null);
}
private void start(@Nullable final IInternalNetworkManagementListener listener) {
if (mIpClient != null) { if (mIpClient != null) {
if (DBG) Log.d(TAG, "IpClient already started"); if (DBG) Log.d(TAG, "IpClient already started");
return; return;
@@ -470,9 +486,10 @@ public class EthernetNetworkFactory extends NetworkFactory {
Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name)); Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name));
} }
mIpClientCallback = new IpClientCallbacksImpl(); mIpClientCallback = new EthernetIpClientCallback(listener);
mDeps.makeIpClient(mContext, name, mIpClientCallback); mDeps.makeIpClient(mContext, name, mIpClientCallback);
mIpClientCallback.awaitIpClientStart(); mIpClientCallback.awaitIpClientStart();
if (sTcpBufferSizes == null) { if (sTcpBufferSizes == null) {
sTcpBufferSizes = mContext.getResources().getString( sTcpBufferSizes = mContext.getResources().getString(
com.android.internal.R.string.config_ethernet_tcp_buffers); com.android.internal.R.string.config_ethernet_tcp_buffers);
@@ -480,8 +497,13 @@ public class EthernetNetworkFactory extends NetworkFactory {
provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes);
} }
void onIpLayerStarted(LinkProperties linkProperties) { void onIpLayerStarted(@NonNull final LinkProperties linkProperties,
@Nullable final IInternalNetworkManagementListener listener) {
if(mIpClient == null) { if(mIpClient == null) {
// This call comes from a message posted on the handler thread, but the IpClient has
// since been stopped such as may be the case if updateInterfaceLinkState() is
// queued on the handler thread prior. As network management callbacks are sent in
// stop(), there is no need to send them again here.
if (DBG) Log.d(TAG, "IpClient is not initialized."); if (DBG) Log.d(TAG, "IpClient is not initialized.");
return; return;
} }
@@ -516,33 +538,53 @@ public class EthernetNetworkFactory extends NetworkFactory {
}); });
mNetworkAgent.register(); mNetworkAgent.register();
mNetworkAgent.markConnected(); mNetworkAgent.markConnected();
sendNetworkManagementCallback(); realizeNetworkManagementCallback(mNetworkAgent.getNetwork(), null);
} }
private void sendNetworkManagementCallback() { void onIpLayerStopped(@Nullable final IInternalNetworkManagementListener listener) {
if (null != mNetworkManagementListener) { // This cannot happen due to provisioning timeout, because our timeout is 0. It can
try { // happen due to errors while provisioning or on provisioning loss.
mNetworkManagementListener.onComplete(mNetworkAgent.getNetwork(), null);
} catch (RemoteException e) {
Log.e(TAG, "Can't send onComplete for network management callback", e);
} finally {
mNetworkManagementListener = null;
}
}
}
void onIpLayerStopped(LinkProperties linkProperties) {
if(mIpClient == null) { if(mIpClient == null) {
if (DBG) Log.d(TAG, "IpClient is not initialized."); if (DBG) Log.d(TAG, "IpClient is not initialized.");
return; return;
} }
// This cannot happen due to provisioning timeout, because our timeout is 0. It can only // There is no point in continuing if the interface is gone as stop() will be triggered
// happen if we're provisioned and we lose provisioning. // by removeInterface() when processed on the handler thread and start() won't
stop(); // work for a non-existent interface.
// If the interface has disappeared provisioning will fail over and over again, so if (null == mDeps.getNetworkInterfaceByName(name)) {
// there is no point in starting again if (DBG) Log.d(TAG, name + " is no longer available.");
if (null != mDeps.getNetworkInterfaceByName(name)) { // Send a callback in case a provisioning request was in progress.
start(); maybeSendNetworkManagementCallbackForAbort();
return;
}
restart(listener);
}
private void maybeSendNetworkManagementCallbackForAbort() {
realizeNetworkManagementCallback(null,
new InternalNetworkManagementException(
"The IP provisioning request has been aborted."));
}
// Must be called on the handler thread
private void realizeNetworkManagementCallback(@Nullable final Network network,
@Nullable final InternalNetworkManagementException e) {
ensureRunningOnEthernetHandlerThread();
if (null == mIpClientCallback) {
return;
}
EthernetNetworkFactory.maybeSendNetworkManagementCallback(
mIpClientCallback.mNetworkManagementListener, network, e);
// Only send a single callback per listener.
mIpClientCallback.mNetworkManagementListener = null;
}
private void ensureRunningOnEthernetHandlerThread() {
if (mHandler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException(
"Not running on the Ethernet thread: "
+ Thread.currentThread().getName());
} }
} }
@@ -577,8 +619,11 @@ public class EthernetNetworkFactory extends NetworkFactory {
if (mLinkUp == up) return false; if (mLinkUp == up) return false;
mLinkUp = up; mLinkUp = up;
if (!up) { // was up, goes down
maybeSendNetworkManagementCallbackForAbort();
stop();
} else { // was down, goes up
stop(); stop();
if (up) {
start(); start();
} }
@@ -628,9 +673,13 @@ public class EthernetNetworkFactory extends NetworkFactory {
} }
void restart() { void restart() {
restart(null);
}
void restart(@Nullable final IInternalNetworkManagementListener listener){
if (DBG) Log.d(TAG, "reconnecting Ethernet"); if (DBG) Log.d(TAG, "reconnecting Ethernet");
stop(); stop();
start(); start(listener);
} }
@Override @Override

View File

@@ -16,8 +16,11 @@
package com.android.server.ethernet; package com.android.server.ethernet;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@@ -63,6 +66,8 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R; import com.android.internal.R;
import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.InterfaceParams;
import com.android.testutils.DevSdkIgnoreRule;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -72,9 +77,7 @@ import org.mockito.MockitoAnnotations;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
@@ -84,7 +87,7 @@ public class EthernetNetworkFactoryTest {
private static final String IP_ADDR = "192.0.2.2/25"; private static final String IP_ADDR = "192.0.2.2/25";
private static final LinkAddress LINK_ADDR = new LinkAddress(IP_ADDR); private static final LinkAddress LINK_ADDR = new LinkAddress(IP_ADDR);
private static final String HW_ADDR = "01:02:03:04:05:06"; private static final String HW_ADDR = "01:02:03:04:05:06";
private final TestLooper mLooper = new TestLooper(); private TestLooper mLooper;
private Handler mHandler; private Handler mHandler;
private EthernetNetworkFactory mNetFactory = null; private EthernetNetworkFactory mNetFactory = null;
private IpClientCallbacks mIpClientCallbacks; private IpClientCallbacks mIpClientCallbacks;
@@ -99,14 +102,18 @@ public class EthernetNetworkFactoryTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mHandler = new Handler(mLooper.getLooper());
mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mDeps);
setupNetworkAgentMock(); setupNetworkAgentMock();
setupIpClientMock(); setupIpClientMock();
setupContext(); setupContext();
} }
//TODO: Move away from usage of TestLooper in order to move this logic back into @Before.
private void initEthernetNetworkFactory() {
mLooper = new TestLooper();
mHandler = new Handler(mLooper.getLooper());
mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mDeps);
}
private void setupNetworkAgentMock() { private void setupNetworkAgentMock() {
when(mDeps.makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any())) when(mDeps.makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()))
.thenAnswer(new AnswerWithArguments() { .thenAnswer(new AnswerWithArguments() {
@@ -288,6 +295,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testAcceptRequest() throws Exception { public void testAcceptRequest() throws Exception {
initEthernetNetworkFactory();
createInterfaceUndergoingProvisioning(TEST_IFACE); createInterfaceUndergoingProvisioning(TEST_IFACE);
assertTrue(mNetFactory.acceptRequest(createDefaultRequest())); assertTrue(mNetFactory.acceptRequest(createDefaultRequest()));
@@ -299,6 +307,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception { public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception {
initEthernetNetworkFactory();
createInterfaceUndergoingProvisioning(TEST_IFACE); createInterfaceUndergoingProvisioning(TEST_IFACE);
// verify that the IpClient gets shut down when interface state changes to down. // verify that the IpClient gets shut down when interface state changes to down.
assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false)); assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
@@ -307,6 +316,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception { public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false)); assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
verifyStop(); verifyStop();
@@ -314,6 +324,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception { public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception {
initEthernetNetworkFactory();
createUnprovisionedInterface(TEST_IFACE); createUnprovisionedInterface(TEST_IFACE);
assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false)); assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
// There should not be an active IPClient or NetworkAgent. // There should not be an active IPClient or NetworkAgent.
@@ -324,6 +335,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception { public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception {
initEthernetNetworkFactory();
// if interface was never added, link state cannot be updated. // if interface was never added, link state cannot be updated.
assertFalse(mNetFactory.updateInterfaceLinkState("eth1", true)); assertFalse(mNetFactory.updateInterfaceLinkState("eth1", true));
verify(mDeps, never()).makeIpClient(any(), any(), any()); verify(mDeps, never()).makeIpClient(any(), any(), any());
@@ -331,6 +343,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testNeedNetworkForOnProvisionedInterface() throws Exception { public void testNeedNetworkForOnProvisionedInterface() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
mNetFactory.needNetworkFor(createDefaultRequest()); mNetFactory.needNetworkFor(createDefaultRequest());
verify(mIpClient, never()).startProvisioning(any()); verify(mIpClient, never()).startProvisioning(any());
@@ -338,6 +351,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testNeedNetworkForOnUnprovisionedInterface() throws Exception { public void testNeedNetworkForOnUnprovisionedInterface() throws Exception {
initEthernetNetworkFactory();
createUnprovisionedInterface(TEST_IFACE); createUnprovisionedInterface(TEST_IFACE);
mNetFactory.needNetworkFor(createDefaultRequest()); mNetFactory.needNetworkFor(createDefaultRequest());
verify(mIpClient).startProvisioning(any()); verify(mIpClient).startProvisioning(any());
@@ -348,6 +362,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testNeedNetworkForOnInterfaceUndergoingProvisioning() throws Exception { public void testNeedNetworkForOnInterfaceUndergoingProvisioning() throws Exception {
initEthernetNetworkFactory();
createInterfaceUndergoingProvisioning(TEST_IFACE); createInterfaceUndergoingProvisioning(TEST_IFACE);
mNetFactory.needNetworkFor(createDefaultRequest()); mNetFactory.needNetworkFor(createDefaultRequest());
verify(mIpClient, never()).startProvisioning(any()); verify(mIpClient, never()).startProvisioning(any());
@@ -358,6 +373,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testProvisioningLoss() throws Exception { public void testProvisioningLoss() throws Exception {
initEthernetNetworkFactory();
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
@@ -369,18 +385,24 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testProvisioningLossForDisappearedInterface() throws Exception { public void testProvisioningLossForDisappearedInterface() throws Exception {
initEthernetNetworkFactory();
// mocked method returns null by default, but just to be explicit in the test: // mocked method returns null by default, but just to be explicit in the test:
when(mDeps.getNetworkInterfaceByName(eq(TEST_IFACE))).thenReturn(null); when(mDeps.getNetworkInterfaceByName(eq(TEST_IFACE))).thenReturn(null);
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
triggerOnProvisioningFailure(); triggerOnProvisioningFailure();
verifyStop();
// the interface disappeared and getNetworkInterfaceByName returns null, we should not retry // the interface disappeared and getNetworkInterfaceByName returns null, we should not retry
verify(mIpClient, never()).startProvisioning(any()); verify(mIpClient, never()).startProvisioning(any());
verify(mNetworkAgent, never()).register();
verify(mIpClient, never()).shutdown();
verify(mNetworkAgent, never()).unregister();
verify(mIpClient, never()).startProvisioning(any());
} }
@Test @Test
public void testIpClientIsNotStartedWhenLinkIsDown() throws Exception { public void testIpClientIsNotStartedWhenLinkIsDown() throws Exception {
initEthernetNetworkFactory();
createUnprovisionedInterface(TEST_IFACE); createUnprovisionedInterface(TEST_IFACE);
mNetFactory.updateInterfaceLinkState(TEST_IFACE, false); mNetFactory.updateInterfaceLinkState(TEST_IFACE, false);
@@ -399,6 +421,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testLinkPropertiesChanged() throws Exception { public void testLinkPropertiesChanged() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
LinkProperties lp = new LinkProperties(); LinkProperties lp = new LinkProperties();
@@ -409,6 +432,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testNetworkUnwanted() throws Exception { public void testNetworkUnwanted() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
mNetworkAgent.getCallbacks().onNetworkUnwanted(); mNetworkAgent.getCallbacks().onNetworkUnwanted();
@@ -418,6 +442,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testNetworkUnwantedWithStaleNetworkAgent() throws Exception { public void testNetworkUnwantedWithStaleNetworkAgent() throws Exception {
initEthernetNetworkFactory();
// ensures provisioning is restarted after provisioning loss // ensures provisioning is restarted after provisioning loss
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
@@ -441,6 +466,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testTransportOverrideIsCorrectlySet() throws Exception { public void testTransportOverrideIsCorrectlySet() throws Exception {
initEthernetNetworkFactory();
// createProvisionedInterface() has verifications in place for transport override // createProvisionedInterface() has verifications in place for transport override
// functionality which for EthernetNetworkFactory is network score and legacy type mappings. // functionality which for EthernetNetworkFactory is network score and legacy type mappings.
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_ETHERNET, createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_ETHERNET,
@@ -461,6 +487,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testReachabilityLoss() throws Exception { public void testReachabilityLoss() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
triggerOnReachabilityLost(); triggerOnReachabilityLost();
@@ -471,6 +498,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testIgnoreOnIpLayerStartedCallbackAfterIpClientHasStopped() throws Exception { public void testIgnoreOnIpLayerStartedCallbackAfterIpClientHasStopped() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
mIpClientCallbacks.onProvisioningFailure(new LinkProperties()); mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
mIpClientCallbacks.onProvisioningSuccess(new LinkProperties()); mIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
@@ -484,6 +512,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testIgnoreOnIpLayerStoppedCallbackAfterIpClientHasStopped() throws Exception { public void testIgnoreOnIpLayerStoppedCallbackAfterIpClientHasStopped() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
mIpClientCallbacks.onProvisioningFailure(new LinkProperties()); mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
@@ -497,26 +526,37 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testIgnoreLinkPropertiesCallbackAfterIpClientHasStopped() throws Exception { public void testIgnoreLinkPropertiesCallbackAfterIpClientHasStopped() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
LinkProperties lp = new LinkProperties(); LinkProperties lp = new LinkProperties();
mIpClientCallbacks.onProvisioningFailure(lp); // The test requires the two proceeding methods to happen one after the other in ENF and
// verifies onLinkPropertiesChange doesn't complete execution for a downed interface.
// Posting is necessary as updateInterfaceLinkState with false will set mIpClientCallbacks
// to null which will throw an NPE in the test if executed synchronously.
mHandler.post(() -> mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
mIpClientCallbacks.onLinkPropertiesChange(lp); mIpClientCallbacks.onLinkPropertiesChange(lp);
mLooper.dispatchAll(); mLooper.dispatchAll();
verifyStop();
verifyStop();
// ipClient has been shut down first, we should not update // ipClient has been shut down first, we should not update
verify(mNetworkAgent, never()).sendLinkPropertiesImpl(same(lp)); verify(mNetworkAgent, never()).sendLinkPropertiesImpl(same(lp));
} }
@Test @Test
public void testIgnoreNeighborLossCallbackAfterIpClientHasStopped() throws Exception { public void testIgnoreNeighborLossCallbackAfterIpClientHasStopped() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
// The test requires the two proceeding methods to happen one after the other in ENF and
// verifies onReachabilityLost doesn't complete execution for a downed interface.
// Posting is necessary as updateInterfaceLinkState with false will set mIpClientCallbacks
// to null which will throw an NPE in the test if executed synchronously.
mHandler.post(() -> mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
mIpClientCallbacks.onReachabilityLost("Neighbor Lost"); mIpClientCallbacks.onReachabilityLost("Neighbor Lost");
mLooper.dispatchAll(); mLooper.dispatchAll();
verifyStop();
verifyStop();
// ipClient has been shut down first, we should not update // ipClient has been shut down first, we should not update
verify(mIpClient, never()).startProvisioning(any()); verify(mIpClient, never()).startProvisioning(any());
verify(mNetworkAgent, never()).register(); verify(mNetworkAgent, never()).register();
@@ -567,6 +607,7 @@ public class EthernetNetworkFactoryTest {
@Test @Test
public void testUpdateInterfaceCallsListenerCorrectlyOnSuccess() throws Exception { public void testUpdateInterfaceCallsListenerCorrectlyOnSuccess() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
final NetworkCapabilities capabilities = createDefaultFilterCaps(); final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig(); final IpConfiguration ipConfiguration = createStaticIpConfig();
@@ -580,8 +621,71 @@ public class EthernetNetworkFactoryTest {
assertNull(ret.second); assertNull(ret.second);
} }
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
@Test
public void testUpdateInterfaceAbortsOnConcurrentRemoveInterface() throws Exception {
initEthernetNetworkFactory();
verifyNetworkManagementCallIsAbortedWhenInterrupted(
TEST_IFACE,
() -> mNetFactory.removeInterface(TEST_IFACE));
}
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
@Test
public void testUpdateInterfaceAbortsOnConcurrentUpdateInterfaceLinkState() throws Exception {
initEthernetNetworkFactory();
verifyNetworkManagementCallIsAbortedWhenInterrupted(
TEST_IFACE,
() -> mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
}
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
@Test
public void testUpdateInterfaceCallsListenerCorrectlyOnConcurrentRequests() throws Exception {
initEthernetNetworkFactory();
final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig();
final TestNetworkManagementListener successfulListener =
new TestNetworkManagementListener();
// If two calls come in before the first one completes, the first listener will be aborted
// and the second one will be successful.
verifyNetworkManagementCallIsAbortedWhenInterrupted(
TEST_IFACE,
() -> {
mNetFactory.updateInterface(
TEST_IFACE, ipConfiguration, capabilities, successfulListener);
triggerOnProvisioningSuccess();
});
final Pair<Network, InternalNetworkManagementException> successfulResult =
successfulListener.expectOnComplete();
assertEquals(mMockNetwork, successfulResult.first);
assertNull(successfulResult.second);
}
private void verifyNetworkManagementCallIsAbortedWhenInterrupted(
@NonNull final String iface,
@NonNull final Runnable interruptingRunnable) throws Exception {
createAndVerifyProvisionedInterface(iface);
final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig();
final TestNetworkManagementListener failedListener = new TestNetworkManagementListener();
// An active update request will be aborted on interrupt prior to provisioning completion.
mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener);
interruptingRunnable.run();
final Pair<Network, InternalNetworkManagementException> failedResult =
failedListener.expectOnComplete();
assertNull(failedResult.first);
assertNotNull(failedResult.second);
assertTrue(failedResult.second.getMessage().contains("aborted"));
}
@Test @Test
public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception { public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE); createAndVerifyProvisionedInterface(TEST_IFACE);
final NetworkCapabilities capabilities = createDefaultFilterCaps(); final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig(); final IpConfiguration ipConfiguration = createStaticIpConfig();