Implementation of Eth Service updateConfiguration
EthernetServiceImpl#updateConfiguration API implementation to allow
for dynamically updating the network capability or ip configuration of
an ethernet based interface.
Bug: 210485380
Test: atest EthernetServiceTests
Change-Id: Idd3b7875a24643d245d0f4bb6f2f4c459898116e
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index d7800c0..b556f6c 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.EthernetNetworkSpecifier;
+import android.net.IInternalNetworkManagementListener;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
@@ -98,14 +99,13 @@
}
}
- public EthernetNetworkFactory(Handler handler, Context context, NetworkCapabilities filter) {
- this(handler, context, filter, new Dependencies());
+ public EthernetNetworkFactory(Handler handler, Context context) {
+ this(handler, context, new Dependencies());
}
@VisibleForTesting
- EthernetNetworkFactory(Handler handler, Context context, NetworkCapabilities filter,
- Dependencies deps) {
- super(handler.getLooper(), context, NETWORK_TYPE, filter);
+ EthernetNetworkFactory(Handler handler, Context context, Dependencies deps) {
+ super(handler.getLooper(), context, NETWORK_TYPE, createDefaultNetworkCapabilities());
mHandler = handler;
mContext = context;
@@ -166,8 +166,9 @@
.toArray(String[]::new);
}
- void addInterface(String ifaceName, String hwAddress, NetworkCapabilities capabilities,
- IpConfiguration ipConfiguration) {
+ void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress,
+ @NonNull final IpConfiguration ipConfig,
+ @NonNull final NetworkCapabilities capabilities) {
if (mTrackingInterfaces.containsKey(ifaceName)) {
Log.e(TAG, "Interface with name " + ifaceName + " already exists.");
return;
@@ -177,14 +178,48 @@
Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + capabilities);
}
- NetworkInterfaceState iface = new NetworkInterfaceState(
- ifaceName, hwAddress, mHandler, mContext, capabilities, this, mDeps);
- iface.setIpConfig(ipConfiguration);
+ final NetworkInterfaceState iface = new NetworkInterfaceState(
+ ifaceName, hwAddress, mHandler, mContext, ipConfig, capabilities, this, mDeps);
mTrackingInterfaces.put(ifaceName, iface);
-
updateCapabilityFilter();
}
+ /**
+ * Update a network's configuration and restart it if necessary.
+ *
+ * @param ifaceName the interface name of the network to be updated.
+ * @param ipConfig the desired {@link IpConfiguration} for the given network.
+ * @param capabilities the desired {@link NetworkCapabilities} for the given network. If
+ * {@code null} is passed, then the network's current
+ * {@link NetworkCapabilities} will be used in support of existing APIs as
+ * the public API does not allow this.
+ * @param listener an optional {@link IInternalNetworkManagementListener} to notify callers of
+ * completion.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected void updateInterface(@NonNull final String ifaceName,
+ @NonNull final IpConfiguration ipConfig,
+ @Nullable final NetworkCapabilities capabilities,
+ @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();
+ }
+ }
+
+ private void enforceInterfaceIsTracked(@NonNull final String ifaceName) {
+ if (!hasInterface(ifaceName)) {
+ throw new UnsupportedOperationException(
+ "Interface with name " + ifaceName + " is not being tracked.");
+ }
+ }
+
private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc,
NetworkCapabilities addedNc) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(nc);
@@ -194,11 +229,7 @@
}
private void updateCapabilityFilter() {
- NetworkCapabilities capabilitiesFilter =
- NetworkCapabilities.Builder.withoutDefaultCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
- .build();
-
+ NetworkCapabilities capabilitiesFilter = createDefaultNetworkCapabilities();
for (NetworkInterfaceState iface: mTrackingInterfaces.values()) {
capabilitiesFilter = mixInCapabilities(capabilitiesFilter, iface.mCapabilities);
}
@@ -207,6 +238,12 @@
setCapabilityFilter(capabilitiesFilter);
}
+ private static NetworkCapabilities createDefaultNetworkCapabilities() {
+ return NetworkCapabilities.Builder
+ .withoutDefaultCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build();
+ }
+
void removeInterface(String interfaceName) {
NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
if (iface != null) {
@@ -230,15 +267,8 @@
return iface.updateLinkState(up);
}
- boolean hasInterface(String interfacName) {
- return mTrackingInterfaces.containsKey(interfacName);
- }
-
- void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
- NetworkInterfaceState network = mTrackingInterfaces.get(iface);
- if (network != null) {
- network.setIpConfig(ipConfiguration);
- }
+ boolean hasInterface(String ifaceName) {
+ return mTrackingInterfaces.containsKey(ifaceName);
}
private NetworkInterfaceState networkForRequest(NetworkRequest request) {
@@ -272,24 +302,26 @@
return network;
}
- private static class NetworkInterfaceState {
+ @VisibleForTesting
+ static class NetworkInterfaceState {
final String name;
private final String mHwAddress;
- private final NetworkCapabilities mCapabilities;
private final Handler mHandler;
private final Context mContext;
private final NetworkFactory mNetworkFactory;
private final Dependencies mDeps;
- private final int mLegacyType;
private static String sTcpBufferSizes = null; // Lazy initialized.
private boolean mLinkUp;
+ private int mLegacyType;
private LinkProperties mLinkProperties = new LinkProperties();
private volatile @Nullable IpClientManager mIpClient;
+ private @NonNull NetworkCapabilities mCapabilities;
private @Nullable IpClientCallbacksImpl mIpClientCallback;
+ private @Nullable IInternalNetworkManagementListener mNetworkManagementListener;
private @Nullable EthernetNetworkAgent mNetworkAgent;
private @Nullable IpConfiguration mIpConfig;
@@ -362,42 +394,17 @@
}
NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context,
- @NonNull NetworkCapabilities capabilities, NetworkFactory networkFactory,
- Dependencies deps) {
+ @NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities,
+ NetworkFactory networkFactory, Dependencies deps) {
name = ifaceName;
+ mIpConfig = Objects.requireNonNull(ipConfig);
mCapabilities = Objects.requireNonNull(capabilities);
+ mLegacyType = getLegacyType(mCapabilities);
mHandler = handler;
mContext = context;
mNetworkFactory = networkFactory;
mDeps = deps;
- final int legacyType;
- int[] transportTypes = mCapabilities.getTransportTypes();
-
- if (transportTypes.length > 0) {
- legacyType = getLegacyType(transportTypes[0]);
- } else {
- // Should never happen as transport is always one of ETHERNET or a valid override
- throw new ConfigurationException("Network Capabilities do not have an associated "
- + "transport type.");
- }
-
mHwAddress = hwAddress;
- mLegacyType = legacyType;
- }
-
- void setIpConfig(IpConfiguration ipConfig) {
- if (Objects.equals(this.mIpConfig, ipConfig)) {
- if (DBG) Log.d(TAG, "ipConfig have not changed,so ignore setIpConfig");
- return;
- }
- this.mIpConfig = ipConfig;
- if (mNetworkAgent != null) {
- restart();
- }
- }
-
- boolean isRestricted() {
- return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
/**
@@ -408,6 +415,52 @@
return sTransports.get(transport, ConnectivityManager.TYPE_NONE);
}
+ private static int getLegacyType(@NonNull final NetworkCapabilities capabilities) {
+ final int[] transportTypes = capabilities.getTransportTypes();
+ if (transportTypes.length > 0) {
+ return getLegacyType(transportTypes[0]);
+ }
+
+ // Should never happen as transport is always one of ETHERNET or a valid override
+ throw new ConfigurationException("Network Capabilities do not have an associated "
+ + "transport type.");
+ }
+
+ private void setCapabilities(@NonNull final NetworkCapabilities capabilities) {
+ mCapabilities = new NetworkCapabilities(capabilities);
+ 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);
+ if (DBG) {
+ Log.d(TAG, "updateInterface, iface: " + name
+ + ", shouldUpdateIpConfig: " + shouldUpdateIpConfig
+ + ", shouldUpdateCapabilities: " + shouldUpdateCapabilities
+ + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig
+ + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities
+ );
+ }
+
+ 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;
+ }
+
+ boolean isRestricted() {
+ return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ }
+
private void start() {
if (mIpClient != null) {
if (DBG) Log.d(TAG, "IpClient already started");
@@ -459,6 +512,19 @@
});
mNetworkAgent.register();
mNetworkAgent.markConnected();
+ sendNetworkManagementCallback();
+ }
+
+ 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) {
@@ -546,7 +612,7 @@
}
void restart(){
- if (DBG) Log.d(TAG, "reconnecting Etherent");
+ if (DBG) Log.d(TAG, "reconnecting Ethernet");
stop();
start();
}
diff --git a/service-t/src/com/android/server/ethernet/EthernetService.java b/service-t/src/com/android/server/ethernet/EthernetService.java
index 467ab67..492a55a 100644
--- a/service-t/src/com/android/server/ethernet/EthernetService.java
+++ b/service-t/src/com/android/server/ethernet/EthernetService.java
@@ -17,11 +17,16 @@
package com.android.server.ethernet;
import android.content.Context;
+import android.net.INetd;
+import android.net.util.NetdService;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.util.Log;
import com.android.server.SystemService;
+import java.util.Objects;
+
public final class EthernetService extends SystemService {
private static final String TAG = "EthernetService";
@@ -32,9 +37,17 @@
super(context);
final HandlerThread handlerThread = new HandlerThread(THREAD_NAME);
handlerThread.start();
+ final Handler handler = handlerThread.getThreadHandler();
+ final EthernetNetworkFactory factory = new EthernetNetworkFactory(handler, context);
mImpl = new EthernetServiceImpl(
- context, handlerThread.getThreadHandler(),
- new EthernetTracker(context, handlerThread.getThreadHandler()));
+ context, handler,
+ new EthernetTracker(context, handler, factory, getNetd()));
+ }
+
+ private INetd getNetd() {
+ final INetd netd = NetdService.getInstance();
+ Objects.requireNonNull(netd, "could not get netd instance");
+ return netd;
}
@Override
diff --git a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
index f107865..b284477 100644
--- a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
@@ -233,6 +233,9 @@
+ ", request=" + request + ", listener=" + listener);
validateNetworkManagementState(iface, "updateConfiguration()");
// TODO: validate that iface is listed in overlay config_ethernet_interfaces
+
+ mTracker.updateConfiguration(
+ iface, request.getIpConfig(), request.getNetworkCapabilities(), listener);
}
@Override
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 9660194..0b6547d 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.IEthernetServiceListener;
+import android.net.IInternalNetworkManagementListener;
import android.net.INetd;
import android.net.ITetheredInterfaceCallback;
import android.net.InterfaceConfigurationParcel;
@@ -34,7 +35,6 @@
import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -118,14 +118,12 @@
}
}
- EthernetTracker(Context context, Handler handler) {
+ EthernetTracker(@NonNull final Context context, @NonNull final Handler handler,
+ @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) {
mContext = context;
mHandler = handler;
-
- // The services we use.
- mNetd = INetd.Stub.asInterface(
- (IBinder) context.getSystemService(Context.NETD_SERVICE));
- Objects.requireNonNull(mNetd, "could not get netd instance");
+ mFactory = factory;
+ mNetd = netd;
// Interface match regex.
updateIfaceMatchRegexp();
@@ -138,9 +136,6 @@
}
mConfigStore = new EthernetConfigStore();
-
- NetworkCapabilities nc = createNetworkCapabilities(true /* clear default capabilities */);
- mFactory = new EthernetNetworkFactory(handler, context, nc);
}
void start() {
@@ -168,11 +163,30 @@
if (DBG) {
Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration);
}
+ writeIpConfiguration(iface, ipConfiguration);
+ mHandler.post(() -> mFactory.updateInterface(iface, ipConfiguration, null, null));
+ }
- mConfigStore.write(iface, ipConfiguration);
- mIpConfigurations.put(iface, ipConfiguration);
+ private void writeIpConfiguration(@NonNull final String iface,
+ @NonNull final IpConfiguration ipConfig) {
+ mConfigStore.write(iface, ipConfig);
+ mIpConfigurations.put(iface, ipConfig);
+ }
- mHandler.post(() -> mFactory.updateIpConfiguration(iface, ipConfiguration));
+ @VisibleForTesting(visibility = PACKAGE)
+ protected void updateConfiguration(@NonNull final String iface,
+ @NonNull final StaticIpConfiguration staticIpConfig,
+ @NonNull final NetworkCapabilities capabilities,
+ @Nullable final IInternalNetworkManagementListener listener) {
+ if (DBG) {
+ Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities
+ + ", staticIpConfig: " + staticIpConfig);
+ }
+ final IpConfiguration ipConfig = createIpConfiguration(staticIpConfig);
+ writeIpConfiguration(iface, ipConfig);
+ mNetworkCapabilities.put(iface, capabilities);
+ mHandler.post(() ->
+ mFactory.updateInterface(iface, ipConfig, capabilities, listener));
}
IpConfiguration getIpConfiguration(String iface) {
@@ -325,7 +339,7 @@
}
Log.d(TAG, "Tracking interface in client mode: " + iface);
- mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);
+ mFactory.addInterface(iface, hwAddress, ipConfiguration, nc);
} else {
maybeUpdateServerModeInterfaceState(iface, true);
}
@@ -460,11 +474,11 @@
NetworkCapabilities nc = createNetworkCapabilities(
!TextUtils.isEmpty(config.mCapabilities) /* clear default capabilities */,
config.mCapabilities, config.mTransport).build();
- mNetworkCapabilities.put(config.mName, nc);
+ mNetworkCapabilities.put(config.mIface, nc);
if (null != config.mIpConfig) {
IpConfiguration ipConfig = parseStaticIpConfiguration(config.mIpConfig);
- mIpConfigurations.put(config.mName, ipConfig);
+ mIpConfigurations.put(config.mIface, ipConfig);
}
}
@@ -493,10 +507,6 @@
return builder.build();
}
- private static NetworkCapabilities createNetworkCapabilities(boolean clearDefaultCapabilities) {
- return createNetworkCapabilities(clearDefaultCapabilities, null, null).build();
- }
-
/**
* Parses a static list of network capabilities
*
@@ -623,10 +633,15 @@
}
}
}
+ return createIpConfiguration(staticIpConfigBuilder.build());
+ }
+
+ static IpConfiguration createIpConfiguration(
+ @NonNull final StaticIpConfiguration staticIpConfig) {
final IpConfiguration ret = new IpConfiguration();
ret.setIpAssignment(IpAssignment.STATIC);
ret.setProxySettings(ProxySettings.NONE);
- ret.setStaticIpConfiguration(staticIpConfigBuilder.build());
+ ret.setStaticIpConfiguration(staticIpConfig);
return ret;
}
@@ -681,14 +696,14 @@
@VisibleForTesting
static class EthernetTrackerConfig {
- final String mName;
+ final String mIface;
final String mCapabilities;
final String mIpConfig;
final String mTransport;
EthernetTrackerConfig(@NonNull final String[] tokens) {
Objects.requireNonNull(tokens, "EthernetTrackerConfig requires non-null tokens");
- mName = tokens[0];
+ mIface = tokens[0];
mCapabilities = tokens.length > 1 ? tokens[1] : null;
mIpConfig = tokens.length > 2 && !TextUtils.isEmpty(tokens[2]) ? tokens[2] : null;
mTransport = tokens.length > 3 ? tokens[3] : null;
diff --git a/tests/ethernet/Android.bp b/tests/ethernet/Android.bp
index 1bc9352..93a8f6c 100644
--- a/tests/ethernet/Android.bp
+++ b/tests/ethernet/Android.bp
@@ -36,6 +36,8 @@
"ethernet-service",
"frameworks-base-testutils",
"mockito-target-minus-junit4",
+ "net-tests-utils",
+ "services.core",
"services.net",
],
}
diff --git a/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index 990ee5f..44ed26c 100644
--- a/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/ethernet/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -16,9 +16,11 @@
package com.android.server.ethernet;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
@@ -36,17 +38,24 @@
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.EthernetNetworkSpecifier;
+import android.net.IInternalNetworkManagementListener;
+import android.net.InternalNetworkManagementException;
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;
@@ -61,10 +70,20 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+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
public class EthernetNetworkFactoryTest {
+ private static final int TIMEOUT_MS = 2_000;
private static final String TEST_IFACE = "test123";
+ 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 Handler mHandler;
private EthernetNetworkFactory mNetFactory = null;
@@ -75,13 +94,13 @@
@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);
mHandler = new Handler(mLooper.getLooper());
- mNetFactory = new EthernetNetworkFactory(mHandler, mContext, createDefaultFilterCaps(),
- mDeps);
+ mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mDeps);
setupNetworkAgentMock();
setupIpClientMock();
@@ -100,6 +119,8 @@
NetworkProvider provider,
EthernetNetworkAgent.Callbacks cb) {
when(mNetworkAgent.getCallbacks()).thenReturn(cb);
+ when(mNetworkAgent.getNetwork())
+ .thenReturn(mMockNetwork);
return mNetworkAgent;
}
}
@@ -185,6 +206,19 @@
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) {
@@ -194,10 +228,11 @@
private void createInterfaceUndergoingProvisioning(
@NonNull final String iface, final int transportType) {
- mNetFactory.addInterface(iface, iface, createInterfaceCapsBuilder(transportType).build(),
- createDefaultIpConfig());
+ final IpConfiguration ipConfig = createDefaultIpConfig();
+ mNetFactory.addInterface(iface, HW_ADDR, ipConfig,
+ createInterfaceCapsBuilder(transportType).build());
assertTrue(mNetFactory.updateInterfaceLinkState(iface, true));
- verifyStart();
+ verifyStart(ipConfig);
clearInvocations(mDeps);
clearInvocations(mIpClient);
}
@@ -431,13 +466,19 @@
triggerOnReachabilityLost();
// Reachability loss should trigger a stop and start, since the interface is still there
- verifyStop();
- verifyStart();
+ verifyRestart(createDefaultIpConfig());
}
- private void verifyStart() {
+ 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(any());
+ verify(mIpClient).startProvisioning(
+ argThat(x -> Objects.equals(x.mStaticIpConfig, ipConfig.getStaticIpConfiguration()))
+ );
}
private void verifyStop() {
@@ -449,4 +490,56 @@
verify(mNetworkAgent).register();
verify(mNetworkAgent).markConnected();
}
+
+ private static final class TestNetworkManagementListener
+ implements IInternalNetworkManagementListener {
+ private final CompletableFuture<Pair<Network, InternalNetworkManagementException>> mDone
+ = new CompletableFuture<>();
+
+ @Override
+ public void onComplete(final Network network,
+ final InternalNetworkManagementException exception) {
+ mDone.complete(new Pair<>(network, exception));
+ }
+
+ Pair<Network, InternalNetworkManagementException> expectOnComplete() throws Exception {
+ return mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ }
+
+ @Test
+ public void testUpdateInterfaceCallsListenerCorrectlyOnSuccess() throws Exception {
+ createAndVerifyProvisionedInterface(TEST_IFACE);
+ final NetworkCapabilities capabilities = createDefaultFilterCaps();
+ final IpConfiguration ipConfiguration = createStaticIpConfig();
+ final TestNetworkManagementListener listener = new TestNetworkManagementListener();
+
+ mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
+ triggerOnProvisioningSuccess();
+
+ final Pair<Network, InternalNetworkManagementException> ret = listener.expectOnComplete();
+ assertEquals(mMockNetwork, ret.first);
+ assertNull(ret.second);
+ }
+
+ @Test
+ public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception {
+ 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);
+ }
}
diff --git a/tests/ethernet/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/ethernet/java/com/android/server/ethernet/EthernetServiceImplTest.java
index 516e574..1c08883 100644
--- a/tests/ethernet/java/com/android/server/ethernet/EthernetServiceImplTest.java
+++ b/tests/ethernet/java/com/android/server/ethernet/EthernetServiceImplTest.java
@@ -18,13 +18,17 @@
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.IInternalNetworkManagementListener;
import android.net.InternalNetworkUpdateRequest;
import android.net.IpConfiguration;
+import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration;
import android.os.Handler;
@@ -41,6 +45,10 @@
@SmallTest
public class EthernetServiceImplTest {
private static final String TEST_IFACE = "test123";
+ private static final InternalNetworkUpdateRequest UPDATE_REQUEST =
+ new InternalNetworkUpdateRequest(
+ new StaticIpConfiguration(), new NetworkCapabilities.Builder().build());
+ private static final IInternalNetworkManagementListener NULL_LISTENER = null;
private EthernetServiceImpl mEthernetServiceImpl;
@Mock private Context mContext;
@Mock private Handler mHandler;
@@ -69,10 +77,8 @@
public void testUpdateConfigurationRejectsWhenEthNotStarted() {
mEthernetServiceImpl.mStarted.set(false);
assertThrows(IllegalStateException.class, () -> {
- final InternalNetworkUpdateRequest r =
- new InternalNetworkUpdateRequest(new StaticIpConfiguration(), null);
-
- mEthernetServiceImpl.updateConfiguration("" /* iface */, r, null /* listener */);
+ mEthernetServiceImpl.updateConfiguration(
+ "" /* iface */, UPDATE_REQUEST, null /* listener */);
});
}
@@ -95,24 +101,21 @@
@Test
public void testUpdateConfigurationRejectsNullIface() {
assertThrows(NullPointerException.class, () -> {
- final InternalNetworkUpdateRequest r =
- new InternalNetworkUpdateRequest(new StaticIpConfiguration(), null);
-
- mEthernetServiceImpl.updateConfiguration(null /* iface */, r, null /* listener */);
+ mEthernetServiceImpl.updateConfiguration(null, UPDATE_REQUEST, NULL_LISTENER);
});
}
@Test
public void testConnectNetworkRejectsNullIface() {
assertThrows(NullPointerException.class, () -> {
- mEthernetServiceImpl.connectNetwork(null /* iface */, null /* listener */);
+ mEthernetServiceImpl.connectNetwork(null /* iface */, NULL_LISTENER);
});
}
@Test
public void testDisconnectNetworkRejectsNullIface() {
assertThrows(NullPointerException.class, () -> {
- mEthernetServiceImpl.disconnectNetwork(null /* iface */, null /* listener */);
+ mEthernetServiceImpl.disconnectNetwork(null /* iface */, NULL_LISTENER);
});
}
@@ -120,10 +123,7 @@
public void testUpdateConfigurationRejectsWithoutAutomotiveFeature() {
toggleAutomotiveFeature(false);
assertThrows(UnsupportedOperationException.class, () -> {
- final InternalNetworkUpdateRequest r =
- new InternalNetworkUpdateRequest(new StaticIpConfiguration(), null);
-
- mEthernetServiceImpl.updateConfiguration("" /* iface */, r, null /* listener */);
+ mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER);
});
}
@@ -131,7 +131,7 @@
public void testConnectNetworkRejectsWithoutAutomotiveFeature() {
toggleAutomotiveFeature(false);
assertThrows(UnsupportedOperationException.class, () -> {
- mEthernetServiceImpl.connectNetwork("" /* iface */, null /* listener */);
+ mEthernetServiceImpl.connectNetwork("" /* iface */, NULL_LISTENER);
});
}
@@ -139,7 +139,7 @@
public void testDisconnectNetworkRejectsWithoutAutomotiveFeature() {
toggleAutomotiveFeature(false);
assertThrows(UnsupportedOperationException.class, () -> {
- mEthernetServiceImpl.disconnectNetwork("" /* iface */, null /* listener */);
+ mEthernetServiceImpl.disconnectNetwork("" /* iface */, NULL_LISTENER);
});
}
@@ -152,10 +152,7 @@
public void testUpdateConfigurationRejectsWithUntrackedIface() {
shouldTrackIface(TEST_IFACE, false);
assertThrows(UnsupportedOperationException.class, () -> {
- final InternalNetworkUpdateRequest r =
- new InternalNetworkUpdateRequest(new StaticIpConfiguration(), null);
-
- mEthernetServiceImpl.updateConfiguration(TEST_IFACE, r, null /* listener */);
+ mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER);
});
}
@@ -163,7 +160,7 @@
public void testConnectNetworkRejectsWithUntrackedIface() {
shouldTrackIface(TEST_IFACE, false);
assertThrows(UnsupportedOperationException.class, () -> {
- mEthernetServiceImpl.connectNetwork(TEST_IFACE, null /* listener */);
+ mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER);
});
}
@@ -171,11 +168,20 @@
public void testDisconnectNetworkRejectsWithUntrackedIface() {
shouldTrackIface(TEST_IFACE, false);
assertThrows(UnsupportedOperationException.class, () -> {
- mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, null /* listener */);
+ mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
});
}
private void shouldTrackIface(@NonNull final String iface, final boolean shouldTrack) {
doReturn(shouldTrack).when(mEthernetTracker).isTrackingInterface(iface);
}
+
+ @Test
+ public void testUpdateConfiguration() {
+ mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER);
+ verify(mEthernetTracker).updateConfiguration(
+ eq(TEST_IFACE),
+ eq(UPDATE_REQUEST.getIpConfig()),
+ eq(UPDATE_REQUEST.getNetworkCapabilities()), eq(NULL_LISTENER));
+ }
}
diff --git a/tests/ethernet/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/ethernet/java/com/android/server/ethernet/EthernetTrackerTest.java
index 6ea2154..5aca2e4 100644
--- a/tests/ethernet/java/com/android/server/ethernet/EthernetTrackerTest.java
+++ b/tests/ethernet/java/com/android/server/ethernet/EthernetTrackerTest.java
@@ -19,20 +19,35 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import android.content.Context;
+import android.content.res.Resources;
import android.net.InetAddresses;
+import android.net.IInternalNetworkManagementListener;
+import android.net.INetd;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration;
+import android.os.HandlerThread;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
+import com.android.testutils.HandlerUtils;
+
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.net.InetAddress;
import java.util.ArrayList;
@@ -40,6 +55,39 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class EthernetTrackerTest {
+ private static final int TIMEOUT_MS = 1_000;
+ private static final String THREAD_NAME = "EthernetServiceThread";
+ private EthernetTracker tracker;
+ private HandlerThread mHandlerThread;
+ @Mock private Context mContext;
+ @Mock private EthernetNetworkFactory mFactory;
+ @Mock private INetd mNetd;
+ @Mock Resources mResources;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ initMockResources();
+ mHandlerThread = new HandlerThread(THREAD_NAME);
+ mHandlerThread.start();
+ tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd);
+ }
+
+ @After
+ public void cleanUp() {
+ mHandlerThread.quitSafely();
+ }
+
+ private void initMockResources() {
+ doReturn("").when(mResources).getString(R.string.config_ethernet_iface_regex);
+ doReturn(new String[0]).when(mResources).getStringArray(R.array.config_ethernet_interfaces);
+ doReturn(mResources).when(mContext).getResources();
+ }
+
+ private void waitForIdle() {
+ HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS);
+ }
+
/**
* Test: Creation of various valid static IP configurations
*/
@@ -246,16 +294,16 @@
@Test
public void testCreateEthernetTrackerConfigReturnsCorrectValue() {
- final String name = "1";
+ final String iface = "1";
final String capabilities = "2";
final String ipConfig = "3";
final String transport = "4";
- final String configString = String.join(";", name, capabilities, ipConfig, transport);
+ final String configString = String.join(";", iface, capabilities, ipConfig, transport);
final EthernetTracker.EthernetTrackerConfig config =
EthernetTracker.createEthernetTrackerConfig(configString);
- assertEquals(name, config.mName);
+ assertEquals(iface, config.mIface);
assertEquals(capabilities, config.mCapabilities);
assertEquals(ipConfig, config.mIpConfig);
assertEquals(transport, config.mTransport);
@@ -266,4 +314,19 @@
assertThrows(NullPointerException.class,
() -> EthernetTracker.createEthernetTrackerConfig(null));
}
+
+ @Test
+ public void testUpdateConfiguration() {
+ final String iface = "testiface";
+ final NetworkCapabilities capabilities = new NetworkCapabilities.Builder().build();
+ final StaticIpConfiguration staticIpConfig = new StaticIpConfiguration();
+ final IInternalNetworkManagementListener listener = null;
+
+ tracker.updateConfiguration(iface, staticIpConfig, capabilities, listener);
+ waitForIdle();
+
+ verify(mFactory).updateInterface(
+ eq(iface), eq(EthernetTracker.createIpConfiguration(staticIpConfig)),
+ eq(capabilities), eq(listener));
+ }
}