Updates so Network Mgmt Callbacks Are Sent am: 724a0aea08
Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/net/ethernet/+/1949595
Change-Id: I02c713ea6b1d173982b7abe45b535fc459fa92bc
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index 7b727a3..c610f00 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -22,10 +22,12 @@
import android.net.ConnectivityManager;
import android.net.EthernetNetworkSpecifier;
import android.net.IInternalNetworkManagementListener;
+import android.net.InternalNetworkManagementException;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
@@ -203,14 +205,9 @@
@Nullable final IInternalNetworkManagementListener listener) {
enforceInterfaceIsTracked(ifaceName);
final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
- // TODO: The listener will have issues if called in quick succession for the same interface
- // 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);
- updateCapabilityFilter();
- }
+ iface.updateInterface(ipConfig, capabilities, listener);
+ mTrackingInterfaces.put(ifaceName, iface);
+ updateCapabilityFilter();
}
private void enforceInterfaceIsTracked(@NonNull final String ifaceName) {
@@ -247,6 +244,7 @@
void removeInterface(String interfaceName) {
NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
if (iface != null) {
+ iface.maybeSendNetworkManagementCallbackForAbort();
iface.stop();
}
@@ -302,6 +300,21 @@
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
static class NetworkInterfaceState {
final String name;
@@ -320,8 +333,7 @@
private volatile @Nullable IpClientManager mIpClient;
private @NonNull NetworkCapabilities mCapabilities;
- private @Nullable IpClientCallbacksImpl mIpClientCallback;
- private @Nullable IInternalNetworkManagementListener mNetworkManagementListener;
+ private @Nullable EthernetIpClientCallback mIpClientCallback;
private @Nullable EthernetNetworkAgent mNetworkAgent;
private @Nullable IpConfiguration mIpConfig;
@@ -348,9 +360,14 @@
long refCount = 0;
- private class IpClientCallbacksImpl extends IpClientCallbacks {
+ private class EthernetIpClientCallback extends IpClientCallbacks {
private final ConditionVariable mIpClientStartCv = new ConditionVariable(false);
private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false);
+ @Nullable IInternalNetworkManagementListener mNetworkManagementListener;
+
+ EthernetIpClientCallback(@Nullable final IInternalNetworkManagementListener listener) {
+ mNetworkManagementListener = listener;
+ }
@Override
public void onIpClientCreated(IIpClient ipClient) {
@@ -368,12 +385,12 @@
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
- mHandler.post(() -> onIpLayerStarted(newLp));
+ mHandler.post(() -> onIpLayerStarted(newLp, mNetworkManagementListener));
}
@Override
public void onProvisioningFailure(LinkProperties newLp) {
- mHandler.post(() -> onIpLayerStopped(newLp));
+ mHandler.post(() -> onIpLayerStopped(mNetworkManagementListener));
}
@Override
@@ -431,30 +448,25 @@
mLegacyType = getLegacyType(mCapabilities);
}
- boolean updateInterface(@NonNull final IpConfiguration ipConfig,
- @Nullable final NetworkCapabilities capabilities) {
- final boolean shouldUpdateIpConfig = !Objects.equals(mIpConfig, ipConfig);
- final boolean shouldUpdateCapabilities = null != capabilities
- && !Objects.equals(mCapabilities, capabilities);
+ void updateInterface(@NonNull final IpConfiguration ipConfig,
+ @Nullable final NetworkCapabilities capabilities,
+ @Nullable final IInternalNetworkManagementListener listener) {
if (DBG) {
Log.d(TAG, "updateInterface, iface: " + name
- + ", shouldUpdateIpConfig: " + shouldUpdateIpConfig
- + ", shouldUpdateCapabilities: " + shouldUpdateCapabilities
+ ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig
+ ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities
+ + ", listener: " + listener
);
}
- if (shouldUpdateIpConfig) { mIpConfig = ipConfig; };
- if (shouldUpdateCapabilities) { setCapabilities(capabilities); };
- if (shouldUpdateIpConfig || shouldUpdateCapabilities) {
- // 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
- // capabilities changes require a restart.
- restart();
- return true;
- }
- return false;
+ mIpConfig = ipConfig;
+ setCapabilities(capabilities);
+ // 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
+ // be required due to the capabilities or ipConfiguration values, not all
+ // capabilities changes require a restart.
+ restart(listener);
}
boolean isRestricted() {
@@ -462,6 +474,10 @@
}
private void start() {
+ start(null);
+ }
+
+ private void start(@Nullable final IInternalNetworkManagementListener listener) {
if (mIpClient != null) {
if (DBG) Log.d(TAG, "IpClient already started");
return;
@@ -470,9 +486,10 @@
Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name));
}
- mIpClientCallback = new IpClientCallbacksImpl();
+ mIpClientCallback = new EthernetIpClientCallback(listener);
mDeps.makeIpClient(mContext, name, mIpClientCallback);
mIpClientCallback.awaitIpClientStart();
+
if (sTcpBufferSizes == null) {
sTcpBufferSizes = mContext.getResources().getString(
com.android.internal.R.string.config_ethernet_tcp_buffers);
@@ -480,8 +497,13 @@
provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes);
}
- void onIpLayerStarted(LinkProperties linkProperties) {
+ void onIpLayerStarted(@NonNull final LinkProperties linkProperties,
+ @Nullable final IInternalNetworkManagementListener listener) {
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.");
return;
}
@@ -516,33 +538,53 @@
});
mNetworkAgent.register();
mNetworkAgent.markConnected();
- sendNetworkManagementCallback();
+ realizeNetworkManagementCallback(mNetworkAgent.getNetwork(), null);
}
- private void sendNetworkManagementCallback() {
- if (null != mNetworkManagementListener) {
- try {
- 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) {
+ void onIpLayerStopped(@Nullable final IInternalNetworkManagementListener listener) {
+ // This cannot happen due to provisioning timeout, because our timeout is 0. It can
+ // happen due to errors while provisioning or on provisioning loss.
if(mIpClient == null) {
if (DBG) Log.d(TAG, "IpClient is not initialized.");
return;
}
- // This cannot happen due to provisioning timeout, because our timeout is 0. It can only
- // happen if we're provisioned and we lose provisioning.
- stop();
- // If the interface has disappeared provisioning will fail over and over again, so
- // there is no point in starting again
- if (null != mDeps.getNetworkInterfaceByName(name)) {
- start();
+ // There is no point in continuing if the interface is gone as stop() will be triggered
+ // by removeInterface() when processed on the handler thread and start() won't
+ // work for a non-existent interface.
+ if (null == mDeps.getNetworkInterfaceByName(name)) {
+ if (DBG) Log.d(TAG, name + " is no longer available.");
+ // Send a callback in case a provisioning request was in progress.
+ 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 @@
if (mLinkUp == up) return false;
mLinkUp = up;
- stop();
- if (up) {
+ if (!up) { // was up, goes down
+ maybeSendNetworkManagementCallbackForAbort();
+ stop();
+ } else { // was down, goes up
+ stop();
start();
}
@@ -627,10 +672,14 @@
.build();
}
- void restart(){
+ void restart() {
+ restart(null);
+ }
+
+ void restart(@Nullable final IInternalNetworkManagementListener listener){
if (DBG) Log.d(TAG, "reconnecting Ethernet");
stop();
- start();
+ start(listener);
}
@Override
diff --git a/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index 52ddf3c..d569990 100644
--- a/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -16,8 +16,11 @@
package com.android.server.ethernet;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -63,6 +66,8 @@
import com.android.internal.R;
import com.android.net.module.util.InterfaceParams;
+import com.android.testutils.DevSdkIgnoreRule;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -72,9 +77,7 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -84,7 +87,7 @@
private static final String IP_ADDR = "192.0.2.2/25";
private static final LinkAddress LINK_ADDR = new LinkAddress(IP_ADDR);
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 EthernetNetworkFactory mNetFactory = null;
private IpClientCallbacks mIpClientCallbacks;
@@ -99,14 +102,18 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mHandler = new Handler(mLooper.getLooper());
- mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mDeps);
-
setupNetworkAgentMock();
setupIpClientMock();
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() {
when(mDeps.makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()))
.thenAnswer(new AnswerWithArguments() {
@@ -288,6 +295,7 @@
@Test
public void testAcceptRequest() throws Exception {
+ initEthernetNetworkFactory();
createInterfaceUndergoingProvisioning(TEST_IFACE);
assertTrue(mNetFactory.acceptRequest(createDefaultRequest()));
@@ -299,6 +307,7 @@
@Test
public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception {
+ initEthernetNetworkFactory();
createInterfaceUndergoingProvisioning(TEST_IFACE);
// verify that the IpClient gets shut down when interface state changes to down.
assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
@@ -307,6 +316,7 @@
@Test
public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
verifyStop();
@@ -314,6 +324,7 @@
@Test
public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception {
+ initEthernetNetworkFactory();
createUnprovisionedInterface(TEST_IFACE);
assertTrue(mNetFactory.updateInterfaceLinkState(TEST_IFACE, false));
// There should not be an active IPClient or NetworkAgent.
@@ -324,6 +335,7 @@
@Test
public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception {
+ initEthernetNetworkFactory();
// if interface was never added, link state cannot be updated.
assertFalse(mNetFactory.updateInterfaceLinkState("eth1", true));
verify(mDeps, never()).makeIpClient(any(), any(), any());
@@ -331,6 +343,7 @@
@Test
public void testNeedNetworkForOnProvisionedInterface() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
mNetFactory.needNetworkFor(createDefaultRequest());
verify(mIpClient, never()).startProvisioning(any());
@@ -338,6 +351,7 @@
@Test
public void testNeedNetworkForOnUnprovisionedInterface() throws Exception {
+ initEthernetNetworkFactory();
createUnprovisionedInterface(TEST_IFACE);
mNetFactory.needNetworkFor(createDefaultRequest());
verify(mIpClient).startProvisioning(any());
@@ -348,6 +362,7 @@
@Test
public void testNeedNetworkForOnInterfaceUndergoingProvisioning() throws Exception {
+ initEthernetNetworkFactory();
createInterfaceUndergoingProvisioning(TEST_IFACE);
mNetFactory.needNetworkFor(createDefaultRequest());
verify(mIpClient, never()).startProvisioning(any());
@@ -358,6 +373,7 @@
@Test
public void testProvisioningLoss() throws Exception {
+ initEthernetNetworkFactory();
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
createAndVerifyProvisionedInterface(TEST_IFACE);
@@ -369,18 +385,24 @@
@Test
public void testProvisioningLossForDisappearedInterface() throws Exception {
+ initEthernetNetworkFactory();
// mocked method returns null by default, but just to be explicit in the test:
when(mDeps.getNetworkInterfaceByName(eq(TEST_IFACE))).thenReturn(null);
createAndVerifyProvisionedInterface(TEST_IFACE);
triggerOnProvisioningFailure();
- verifyStop();
+
// the interface disappeared and getNetworkInterfaceByName returns null, we should not retry
verify(mIpClient, never()).startProvisioning(any());
+ verify(mNetworkAgent, never()).register();
+ verify(mIpClient, never()).shutdown();
+ verify(mNetworkAgent, never()).unregister();
+ verify(mIpClient, never()).startProvisioning(any());
}
@Test
public void testIpClientIsNotStartedWhenLinkIsDown() throws Exception {
+ initEthernetNetworkFactory();
createUnprovisionedInterface(TEST_IFACE);
mNetFactory.updateInterfaceLinkState(TEST_IFACE, false);
@@ -399,6 +421,7 @@
@Test
public void testLinkPropertiesChanged() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
LinkProperties lp = new LinkProperties();
@@ -409,6 +432,7 @@
@Test
public void testNetworkUnwanted() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
mNetworkAgent.getCallbacks().onNetworkUnwanted();
@@ -418,6 +442,7 @@
@Test
public void testNetworkUnwantedWithStaleNetworkAgent() throws Exception {
+ initEthernetNetworkFactory();
// ensures provisioning is restarted after provisioning loss
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
createAndVerifyProvisionedInterface(TEST_IFACE);
@@ -441,6 +466,7 @@
@Test
public void testTransportOverrideIsCorrectlySet() throws Exception {
+ initEthernetNetworkFactory();
// createProvisionedInterface() has verifications in place for transport override
// functionality which for EthernetNetworkFactory is network score and legacy type mappings.
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_ETHERNET,
@@ -461,6 +487,7 @@
@Test
public void testReachabilityLoss() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
triggerOnReachabilityLost();
@@ -471,6 +498,7 @@
@Test
public void testIgnoreOnIpLayerStartedCallbackAfterIpClientHasStopped() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
mIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
@@ -484,6 +512,7 @@
@Test
public void testIgnoreOnIpLayerStoppedCallbackAfterIpClientHasStopped() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
@@ -497,26 +526,37 @@
@Test
public void testIgnoreLinkPropertiesCallbackAfterIpClientHasStopped() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
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);
mLooper.dispatchAll();
- verifyStop();
+ verifyStop();
// ipClient has been shut down first, we should not update
verify(mNetworkAgent, never()).sendLinkPropertiesImpl(same(lp));
}
@Test
public void testIgnoreNeighborLossCallbackAfterIpClientHasStopped() throws Exception {
+ initEthernetNetworkFactory();
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");
mLooper.dispatchAll();
- verifyStop();
+ verifyStop();
// ipClient has been shut down first, we should not update
verify(mIpClient, never()).startProvisioning(any());
verify(mNetworkAgent, never()).register();
@@ -567,6 +607,7 @@
@Test
public void testUpdateInterfaceCallsListenerCorrectlyOnSuccess() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig();
@@ -580,8 +621,71 @@
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
public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception {
+ initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig();