blob: 28fcf3e80ea0b337eebc308f596fe41cc444c4d3 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.EthernetNetworkSpecifier;
import android.net.IEthernetNetworkManagementListener;
import android.net.EthernetNetworkManagementException;
import android.net.IpConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.StaticIpConfiguration;
import android.net.ip.IpClientCallbacks;
import android.net.ip.IpClientManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.test.TestLooper;
import android.util.Pair;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
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;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class EthernetNetworkFactoryTest {
private static final int TIMEOUT_MS = 2_000;
private static final String TEST_IFACE = "test123";
private static final IEthernetNetworkManagementListener NULL_LISTENER = null;
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 TestLooper mLooper;
private Handler mHandler;
private EthernetNetworkFactory mNetFactory = null;
private IpClientCallbacks mIpClientCallbacks;
@Mock private Context mContext;
@Mock private Resources mResources;
@Mock private EthernetNetworkFactory.Dependencies mDeps;
@Mock private IpClientManager mIpClient;
@Mock private EthernetNetworkAgent mNetworkAgent;
@Mock private InterfaceParams mInterfaceParams;
@Mock private Network mMockNetwork;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
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() {
public EthernetNetworkAgent answer(
Context context,
Looper looper,
NetworkCapabilities nc,
LinkProperties lp,
NetworkAgentConfig config,
NetworkProvider provider,
EthernetNetworkAgent.Callbacks cb) {
when(mNetworkAgent.getCallbacks()).thenReturn(cb);
when(mNetworkAgent.getNetwork())
.thenReturn(mMockNetwork);
return mNetworkAgent;
}
}
);
}
private void setupIpClientMock() throws Exception {
doAnswer(inv -> {
// these tests only support one concurrent IpClient, so make sure we do not accidentally
// create a mess.
assertNull("An IpClient has already been created.", mIpClientCallbacks);
mIpClientCallbacks = inv.getArgument(2);
mIpClientCallbacks.onIpClientCreated(null);
mLooper.dispatchAll();
return null;
}).when(mDeps).makeIpClient(any(Context.class), anyString(), any());
doAnswer(inv -> {
mIpClientCallbacks.onQuit();
mLooper.dispatchAll();
mIpClientCallbacks = null;
return null;
}).when(mIpClient).shutdown();
when(mDeps.makeIpClientManager(any())).thenReturn(mIpClient);
}
private void triggerOnProvisioningSuccess() {
mIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
mLooper.dispatchAll();
}
private void triggerOnProvisioningFailure() {
mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
mLooper.dispatchAll();
}
private void triggerOnReachabilityLost() {
mIpClientCallbacks.onReachabilityLost("ReachabilityLost");
mLooper.dispatchAll();
}
private void setupContext() {
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getString(R.string.config_ethernet_tcp_buffers)).thenReturn("");
}
@After
public void tearDown() {
// looper is shared with the network agents, so there may still be messages to dispatch on
// tear down.
mLooper.dispatchAll();
}
private NetworkCapabilities createDefaultFilterCaps() {
return NetworkCapabilities.Builder.withoutDefaultCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.build();
}
private NetworkCapabilities.Builder createInterfaceCapsBuilder(final int transportType) {
return new NetworkCapabilities.Builder()
.addTransportType(transportType)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
private NetworkRequest.Builder createDefaultRequestBuilder() {
return new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
private NetworkRequest createDefaultRequest() {
return createDefaultRequestBuilder().build();
}
private IpConfiguration createDefaultIpConfig() {
IpConfiguration ipConfig = new IpConfiguration();
ipConfig.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
ipConfig.setProxySettings(IpConfiguration.ProxySettings.NONE);
return ipConfig;
}
/**
* Create an {@link IpConfiguration} with an associated {@link StaticIpConfiguration}.
*
* @return {@link IpConfiguration} with its {@link StaticIpConfiguration} set.
*/
private IpConfiguration createStaticIpConfig() {
final IpConfiguration ipConfig = new IpConfiguration();
ipConfig.setIpAssignment(IpConfiguration.IpAssignment.STATIC);
ipConfig.setStaticIpConfiguration(
new StaticIpConfiguration.Builder().setIpAddress(LINK_ADDR).build());
return ipConfig;
}
// creates an interface with provisioning in progress (since updating the interface link state
// automatically starts the provisioning process)
private void createInterfaceUndergoingProvisioning(String iface) {
// Default to the ethernet transport type.
createInterfaceUndergoingProvisioning(iface, NetworkCapabilities.TRANSPORT_ETHERNET);
}
private void createInterfaceUndergoingProvisioning(
@NonNull final String iface, final int transportType) {
final IpConfiguration ipConfig = createDefaultIpConfig();
mNetFactory.addInterface(iface, HW_ADDR, ipConfig,
createInterfaceCapsBuilder(transportType).build());
assertTrue(mNetFactory.updateInterfaceLinkState(iface, true, NULL_LISTENER));
verifyStart(ipConfig);
clearInvocations(mDeps);
clearInvocations(mIpClient);
}
// creates a provisioned interface
private void createAndVerifyProvisionedInterface(String iface) throws Exception {
// Default to the ethernet transport type.
createAndVerifyProvisionedInterface(iface, NetworkCapabilities.TRANSPORT_ETHERNET,
ConnectivityManager.TYPE_ETHERNET);
}
private void createVerifyAndRemoveProvisionedInterface(final int transportType,
final int expectedLegacyType) throws Exception {
createAndVerifyProvisionedInterface(TEST_IFACE, transportType,
expectedLegacyType);
mNetFactory.removeInterface(TEST_IFACE);
}
private void createAndVerifyProvisionedInterface(
@NonNull final String iface, final int transportType, final int expectedLegacyType)
throws Exception {
createInterfaceUndergoingProvisioning(iface, transportType);
triggerOnProvisioningSuccess();
// provisioning succeeded, verify that the network agent is created, registered, marked
// as connected and legacy type are correctly set.
final ArgumentCaptor<NetworkCapabilities> ncCaptor = ArgumentCaptor.forClass(
NetworkCapabilities.class);
verify(mDeps).makeEthernetNetworkAgent(any(), any(), ncCaptor.capture(), any(),
argThat(x -> x.getLegacyType() == expectedLegacyType), any(), any());
assertEquals(
new EthernetNetworkSpecifier(iface), ncCaptor.getValue().getNetworkSpecifier());
verifyNetworkAgentRegistersAndConnects();
clearInvocations(mDeps);
clearInvocations(mNetworkAgent);
}
// creates an unprovisioned interface
private void createUnprovisionedInterface(String iface) throws Exception {
// 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
// then calling onNetworkUnwanted.
createAndVerifyProvisionedInterface(iface);
mNetworkAgent.getCallbacks().onNetworkUnwanted();
mLooper.dispatchAll();
verifyStop();
clearInvocations(mIpClient);
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
public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception {
initEthernetNetworkFactory();
createInterfaceUndergoingProvisioning(TEST_IFACE);
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
// verify that the IpClient gets shut down when interface state changes to down.
final boolean ret =
mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener);
assertTrue(ret);
verify(mIpClient).shutdown();
assertSuccessfulListener(listener, null);
}
@Test
public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
final boolean ret =
mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener);
assertTrue(ret);
verifyStop();
assertSuccessfulListener(listener, mMockNetwork);
}
@Test
public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception {
initEthernetNetworkFactory();
createUnprovisionedInterface(TEST_IFACE);
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
final boolean ret =
mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener);
assertTrue(ret);
// There should not be an active IPClient or NetworkAgent.
verify(mDeps, never()).makeIpClient(any(), any(), any());
verify(mDeps, never())
.makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
assertSuccessfulListener(listener, null);
}
@Test
public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception {
initEthernetNetworkFactory();
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
// if interface was never added, link state cannot be updated.
final boolean ret =
mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listener);
assertFalse(ret);
verifyNoStopOrStart();
assertFailedListener(listener, "can't be updated as it is not available");
}
@Test
public void testUpdateInterfaceLinkStateWithNoChanges() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
final boolean ret =
mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listener);
assertFalse(ret);
verifyNoStopOrStart();
assertFailedListener(listener, "No changes");
}
@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
public void testProvisioningLoss() throws Exception {
initEthernetNetworkFactory();
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
createAndVerifyProvisionedInterface(TEST_IFACE);
triggerOnProvisioningFailure();
verifyStop();
// provisioning loss should trigger a retry, since the interface is still there
verify(mIpClient).startProvisioning(any());
}
@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();
// the interface disappeared and getNetworkInterfaceByName returns null, we should not retry
verify(mIpClient, never()).startProvisioning(any());
verifyNoStopOrStart();
}
private void verifyNoStopOrStart() {
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, 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
public void testLinkPropertiesChanged() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
LinkProperties lp = new LinkProperties();
mIpClientCallbacks.onLinkPropertiesChange(lp);
mLooper.dispatchAll();
verify(mNetworkAgent).sendLinkPropertiesImpl(same(lp));
}
@Test
public void testNetworkUnwanted() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
mNetworkAgent.getCallbacks().onNetworkUnwanted();
mLooper.dispatchAll();
verifyStop();
}
@Test
public void testNetworkUnwantedWithStaleNetworkAgent() throws Exception {
initEthernetNetworkFactory();
// ensures provisioning is restarted after provisioning loss
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
createAndVerifyProvisionedInterface(TEST_IFACE);
EthernetNetworkAgent.Callbacks oldCbs = mNetworkAgent.getCallbacks();
// replace network agent in EthernetNetworkFactory
// Loss of provisioning will restart the ip client and network agent.
triggerOnProvisioningFailure();
verify(mDeps).makeIpClient(any(), any(), any());
triggerOnProvisioningSuccess();
verify(mDeps).makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
// verify that unwanted is ignored
clearInvocations(mIpClient);
clearInvocations(mNetworkAgent);
oldCbs.onNetworkUnwanted();
verify(mIpClient, never()).shutdown();
verify(mNetworkAgent, never()).unregister();
}
@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,
ConnectivityManager.TYPE_ETHERNET);
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_BLUETOOTH,
ConnectivityManager.TYPE_BLUETOOTH);
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI,
ConnectivityManager.TYPE_WIFI);
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_CELLULAR,
ConnectivityManager.TYPE_MOBILE);
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_LOWPAN,
ConnectivityManager.TYPE_NONE);
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI_AWARE,
ConnectivityManager.TYPE_NONE);
createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_TEST,
ConnectivityManager.TYPE_NONE);
}
@Test
public void testReachabilityLoss() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
triggerOnReachabilityLost();
// Reachability loss should trigger a stop and start, since the interface is still there
verifyRestart(createDefaultIpConfig());
}
@Test
public void testIgnoreOnIpLayerStartedCallbackAfterIpClientHasStopped() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
mIpClientCallbacks.onProvisioningSuccess(new LinkProperties());
mLooper.dispatchAll();
verifyStop();
// ipClient has been shut down first, we should not retry
verify(mIpClient, never()).startProvisioning(any());
verify(mNetworkAgent, never()).register();
}
@Test
public void testIgnoreOnIpLayerStoppedCallbackAfterIpClientHasStopped() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
mIpClientCallbacks.onProvisioningFailure(new LinkProperties());
mLooper.dispatchAll();
verifyStop();
// ipClient has been shut down first, we should not retry
verify(mIpClient).startProvisioning(any());
}
@Test
public void testIgnoreLinkPropertiesCallbackAfterIpClientHasStopped() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
LinkProperties lp = new LinkProperties();
// 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, NULL_LISTENER));
mIpClientCallbacks.onLinkPropertiesChange(lp);
mLooper.dispatchAll();
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);
// 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, NULL_LISTENER));
mIpClientCallbacks.onReachabilityLost("Neighbor Lost");
mLooper.dispatchAll();
verifyStop();
// ipClient has been shut down first, we should not update
verify(mIpClient, never()).startProvisioning(any());
verify(mNetworkAgent, never()).register();
}
private void verifyRestart(@NonNull final IpConfiguration ipConfig) {
verifyStop();
verifyStart(ipConfig);
}
private void verifyStart(@NonNull final IpConfiguration ipConfig) {
verify(mDeps).makeIpClient(any(Context.class), anyString(), any());
verify(mIpClient).startProvisioning(
argThat(x -> Objects.equals(x.mStaticIpConfig, ipConfig.getStaticIpConfiguration()))
);
}
private void verifyStop() {
verify(mIpClient).shutdown();
verify(mNetworkAgent).unregister();
}
private void verifyNetworkAgentRegistersAndConnects() {
verify(mNetworkAgent).register();
verify(mNetworkAgent).markConnected();
}
private static final class TestNetworkManagementListener
implements IEthernetNetworkManagementListener {
private final CompletableFuture<Pair<Network, EthernetNetworkManagementException>> mDone
= new CompletableFuture<>();
@Override
public void onComplete(final Network network,
final EthernetNetworkManagementException exception) {
mDone.complete(new Pair<>(network, exception));
}
Pair<Network, EthernetNetworkManagementException> expectOnComplete() throws Exception {
return mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
@Override
public IBinder asBinder() {
return null;
}
}
@Test
public void testUpdateInterfaceCallsListenerCorrectlyOnSuccess() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig();
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
triggerOnProvisioningSuccess();
assertSuccessfulListener(listener, mMockNetwork);
}
@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, NULL_LISTENER));
}
@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();
});
assertSuccessfulListener(successfulListener, mMockNetwork);
}
private void assertSuccessfulListener(
@NonNull final TestNetworkManagementListener successfulListener,
@NonNull final Network expectedNetwork) throws Exception {
final Pair<Network, EthernetNetworkManagementException> successfulResult =
successfulListener.expectOnComplete();
assertEquals(expectedNetwork, successfulResult.first);
assertNull(successfulResult.second);
}
private void assertFailedListener(@NonNull final TestNetworkManagementListener failedListener,
@NonNull final String errMsg)
throws Exception {
final Pair<Network, EthernetNetworkManagementException> failedResult =
failedListener.expectOnComplete();
assertNull(failedResult.first);
assertNotNull(failedResult.second);
assertTrue(failedResult.second.getMessage().contains(errMsg));
}
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();
assertFailedListener(failedListener, "aborted");
}
@Test
public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig();
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
triggerOnProvisioningSuccess();
listener.expectOnComplete();
verify(mDeps).makeEthernetNetworkAgent(any(), any(),
eq(capabilities), any(), any(), any(), any());
verifyRestart(ipConfiguration);
}
@Test
public void testUpdateInterfaceForNonExistingInterface() throws Exception {
initEthernetNetworkFactory();
// No interface exists due to not calling createAndVerifyProvisionedInterface(...).
final NetworkCapabilities capabilities = createDefaultFilterCaps();
final IpConfiguration ipConfiguration = createStaticIpConfig();
final TestNetworkManagementListener listener = new TestNetworkManagementListener();
mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
verifyNoStopOrStart();
assertFailedListener(listener, "can't be updated as it is not available");
}
}